1.模式定义:

单例模式确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

2.模式特点:

(1)单例类只能有一个实例。
(2)单例类必须自己创建自己的唯一实例。
(3)单例类必须给所有其他对象提供这一实例。

3.使用场景:

定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
(1)需要频繁实例化然后销毁的对象。
(2)创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
(3)有状态的工具类对象。
(4)频繁访问数据库或文件的对象。
(5)以及其他我没用过的所有要求只有一个对象的场景。

4.模式实现(八种方式):

  (1)饿汉式(静态常量)[可用]

public class Singleton { 

  private final static Singleton INSTANCE = new Singleton();   private Singleton(){}   public static Singleton getInstance(){     return INSTANCE;   } }

优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

  (2)饿汉式(静态代码块)[可用]

public class Singleton {   private static Singleton instance;   static {    instance = new Singleton();   }   private Singleton() {}  public Singleton getInstance() {    return instance;   } }

这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。

  (3)懒汉式(线程不安全)[不可用]

public class Singleton {  private static Singleton singleton;   private Singleton() {}   public static Singleton getInstance() {     if (singleton == null) {       singleton = new Singleton();     }     return singleton;   } }

这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。

  (4)懒汉式(线程安全,同步方法)[不推荐用]

public class Singleton {  private static Singleton singleton;   private Singleton() {}   public static synchronized Singleton getInstance() {     if (singleton == null) {       singleton = new Singleton();     }     return singleton;   } }

优点:解决上面第三种实现方式的线程不安全问题,做个线程同步就可以了,于是就对getInstance()方法进行了线程同步。
缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低要改进。

  (5)懒汉式(线程安全,同步代码块)[不可用]

public class Singleton {   private static Singleton singleton;  private Singleton() {}   public static Singleton getInstance() {     if (singleton == null) {       synchronized (Singleton.class) {         singleton = new Singleton();       }     }     return singleton;   } }

由于第四种实现方式同步效率太低,所以摒弃同步方法,改为同步产生实例化的的代码块。但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。

  (6)双重检查[推荐用]

public class Singleton {   private static volatile Singleton singleton;   private Singleton() {}   public static Singleton getInstance() {     if (singleton == null) {      synchronized (Singleton.class) {         if (singleton == null) {           singleton = new Singleton();        }       }     }     return singleton;   } }

Double-Check概念对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象。
优点:线程安全;延迟加载;效率较高。

  (7).静态内部类[推荐用]

public class Singleton {   private Singleton() {}   private static class SingletonInstance {     private static final Singleton INSTANCE = new Singleton();   }   public static Singleton getInstance() {    return SingletonInstance.INSTANCE;   } }

这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
优点:避免了线程不安全,延迟加载,效率高。

  (8).枚举[推荐用]

public enum Singleton {   INSTANCE;   public void whateverMethod() { } }

借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。可能是因为枚举在JDK1.5中才添加,所以在实际项目开发中,很少见人这么写过。

5.优缺点:

(1)单例模式的优点:

[1]在内存中只有一个对象,节省内存空间。
[2]避免频繁的创建销毁对象,可以提高性能。
[3]避免对共享资源的多重占用。
[4]可以全局访问。
[5]灵活性:因为类控制了实例化过程,所以类可以灵活更改实例化过程。
[6]实例控制:会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。

(2)单例模式的缺点:

[1]开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
[2]可能的开发混淆:使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
[3]对象生存期:不能解决删除单个对象的问题。

6.注意事项

(1)只能使用单例类提供的方法得到单例对象,不要使用反射,否则将会实例化一个新对象。
(2)不要做断开单例类对象与类中静态引用的危险操作。
(3)多线程使用单例使用共享资源时,注意线程安全问题。

7.相关问题

(1)单例模式的对象长时间不用会被jvm垃圾收集器收集吗

除非人为地断开单例中静态引用到单例对象的联接,否则jvm垃圾收集器是不会回收单例对象的。

(2)在一个jvm中会出现多个单例吗

在分布式系统、多个类加载器、以及序列化的的情况下,会产生多个单例,这一点是无庸置疑的。那么在同一个jvm中,会不会产生单例呢?使用单例提供的getInstance()方法只能得到同一个单例,除非是使用反射方式,将会得到新的单例。代码如下
Class c = Class.forName(Singleton.class.getName());
Constructor ct = c.getDeclaredConstructor();
ct.setAccessible(true);
Singleton singleton = (Singleton)ct.newInstance();  

这样,每次运行都会产生新的单例对象。所以运用单例模式时,一定注意不要使用反射产生新的单例对象。

(3)懒汉式单例线程安全吗

主要是网上的一些说法,懒汉式的单例模式是线程不安全的,即使是在实例化对象的方法上加synchronized关键字,也依然是危险的,但是笔者经过编码测试,发现加synchronized关键字修饰后,虽然对性能有部分影响,但是却是线程安全的,并不会产生实例化多个对象的情况。

  (4)单例模式只有饿汉式和懒汉式两种吗

饿汉式单例和懒汉式单例只是两种比较主流和常用的单例模式方法,从理论上讲,任何可以实现一个类只有一个实例的设计模式,都可以称为单例模式。

(5)单例类可以被继承吗

饿汉式单例和懒汉式单例由于构造方法是private的,所以他们都是不可继承的,但是其他很多单例模式是可以继承的,例如登记式单例。

(6)饿汉式单例好还是懒汉式单例好

在java中,饿汉式单例要优于懒汉式单例。C++中则一般使用懒汉式单例。

转载于:https://www.cnblogs.com/yysbolg/p/7380568.html

设计模式(2)--Singleton(单例模式)--创建型相关推荐

  1. 设计模式(二)—— 创建型模式

    设计模式(二)-- 创建型模式 文章首发于 掘金 作者:MiyueFE 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 创建型模式,即处理对象创建过程的设计模式,根据实际情况来使 ...

  2. 本文详细介绍Python 设计模式系列之二: 创建型 Simple Factory 模式(转载)

    源地址:http://doc.chinaunix.net/python/200703/202210.shtml 本文详细介绍Python 设计模式系列之二: 创建型 Simple Factory 模式 ...

  3. 《设计模式》读书笔记——创建型模式

    设计模式 创建模式 定义: 创建型模式抽象了实例化过程.他们帮助一个系统独立于如何创建.组合和表示它的那些对象 一个类创建型模式使用继承改变被实例化的类,而一个对象创建模式是将实例化委托给另一个对象 ...

  4. Java设计模式(二)创建型设计模式

    文章目录 三 创建型设计模式 3.1 单例设计模式 3.1.1 饿汉式(线程安全) 3.1.2 懒汉式(线程不安全) 3.1.3 优缺点 3.1.4 补充 3.1.5 框架中的使用 3.1.4.1 S ...

  5. Java设计模式学习1:创建型与结构型

    设计模式介绍 首先要明白设计模式到底是什么,设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结,其中最出名的当属 Gang of Four (GoF) 的分类了,他们将设计模式分类为 23 种 ...

  6. JAVA设计模式第二讲:创建型设计模式

    设计模式(design pattern)是对软件设计中普遍存在的各种问题,所提出的解决方案.本文以面试题作为切入点,介绍了设计模式的常见问题.我们需要掌握各种设计模式的原理.实现.设计意图和应用场景, ...

  7. 设计模式(三)创建型模式

    前言 根据菜鸟教程的目录,我们首先来看看创建型模式. 创建型模式研究: 实际应用中通常有哪些不同的创建对象的场景: 在不同的场景下,如何更好地编写创建对象的代码. 主要研究构造函数. 下面分别对创建型 ...

  8. 《设计模式详解》创建型模式 - 建造者模式

    建造者模式 4.4 建造者模式 4.4.1 概述 4.4.2 结构 4.4.3 实例 4.4.4 优缺点 4.4.5 使用场景 4.4.6 扩展 - 构建对象 重构前 重构后 4.5 创建型模式对比 ...

  9. 《设计模式详解》创建型模式 - 原型模式

    原型模式 4.3 原型模式 4.3.1 概述 4.3.2 结构 4.3.3 实现 4.3.4 案例 4.3.5 使用场景 4.3.6 扩展 - 深克隆 引用对象的浅克隆 实现 1:文件流 + 对象流 ...

最新文章

  1. 电视游戏会是未来客厅娱乐的主角吗?
  2. HTML:基本的标签
  3. 互逆的压缩与解压(洛谷P1319、P1320题题解,Java语言描述)
  4. 【redis】Redis简介
  5. 【每日算法Day 101】字节跳动 AI Lab 精选面试编程题
  6. bind9的初步使用(2)
  7. 利用R语言进行主成分分析的步骤
  8. Oracle 、SqlServer 根据日期逐日、逐月递增累加、逐行累加
  9. STM32学习笔记(一)
  10. 计划驱动的软件过程:waterfall model(瀑布模型,或称之为SDLC模型)
  11. Python制作专属有声小说(调用百度语音合成接口)
  12. == 和equals的比较
  13. channel通道的一些细节
  14. 使用 canvas 和面向对象思想绘制流星动画
  15. 可能不能吃的食物-为自己记录一下
  16. php warning require,Thinkphp 网站打开提示Warning: require(): open_basedir restriction in effect另一种解决方法...
  17. 华为董事会名单大曝光:孙亚芳任正非等13名董事
  18. TC---教学知识与能力
  19. 云计算统一办公运营平台服务能力设计方案
  20. 科全进销存系统单机永久免费版

热门文章

  1. Linux常用指令和系统管理命令总结
  2. # PHP - 使用PHPMailer发邮件
  3. Vmware 中安装Unix
  4. 在WPF的WebBrowser控件中抑制脚本错误
  5. ADO.Net知识总结
  6. JavaScript中Map的应用及Map中的bug
  7. NSLog (Log信息的输出)
  8. python学习之--习题集
  9. ubuntu一般软件安装在什么目录
  10. 三种流行防火墙配置方案分析与对比