剑指offer第二版(Java最优解)---实现单例模式(3种)

单例模式的定义

单例模式最初的定义出现于《设计模式》(艾迪生维斯理,1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”
  另一个常见的定义是:一个类只有一个实例,并且自行实例化向整个系统提供。
  这两句话的意思就是,当我们需要用到某个实例的时候,我们无需进行其它多余操作,而是直接通过某个接口获取到它的实例,并且这个实例在整个系统中保证唯一。
  举个简单的例子:我们在平时使用电脑时,我们希望点击“设置”按钮,就可以直接访问设置,而且要求设置在整个系统中是唯一的(这是废话),电脑的设置在这里就是一个单例。
  
  我们通过定义,得出完成单例模式需要满足下面两个条件:
  
  1. 生成类的实例要唯一。也就是生成代码只能执行一次,“阻止”所有想要生成新对象的操作;
  2. 生成实例的方法必须是全局方法(也就是静态)。原因是非静态方法必须通过实例进行调用,如果已经有了实例,我们还需要生成实例的方法干什么呢?

饿汉式


  
  优点:线程安全,代码简单;
  
  缺点:不能延迟加载,系统性能会有所降低。

懒汉式

双重判断被称为双重检查加锁(DCL,double check lock)。
 
  其中用了两个if() 判断,第一个if 先判断singleton 是否为null :如果不为null ,说明singleton 已经被初始化了,直接返回singleton ;
  
  如果singleton 为null ,说明singleton 还没有被初始化,这样才会去执行synchronized 修饰的代码块内容,只在其初始化的时候调用一次。这样的设计既能保证只产生一个实例,并且只在初始化的时候加同步锁,也实现了延迟加载。

对于synchronized 关键字,当一个线程访问对象的一个synchronized(xx.class) 同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(xx.class) 同步代码块。
 
 volatile 关键字禁止指令重排序的做法是在对被其修饰的变量进行操作时,增加一个内存屏障(Memory Barrier或Memory Fence,指重排序时不能把后面的指令重排序到内存屏障之前的的位置)用以保证一致性。这样我们就解决了指令重排序的问题。

优点:能延迟加载,也能保证线程安全;

缺点:代码较复杂。

延迟初始化占位(Holder)类模式(推荐)

这种方式成为延迟初始化占位(Holder)类模式。该模式引入了一个内部静态类(占位类),内部静态类只有在调用时才会加载,既保证了Ex02Singleton 实例的延迟初始化,又保证了实例的唯一性。是一种提前初始化(饿汉式)和延迟初始化(饱汉式)的综合模式,推荐使用这种操作。

这种方法基于在懒汉模式中提出的,JVM在类的初始化阶段给出的线程安全性保证,将singleton 的实例化操作放置到一个静态内部类中,在第一次调用getInstance() 方法时,JVM才会去加载InstanceHolder 类,同时初始化singleton 实例,因此,即使我们不采取任何同步策略,getInstance() 方法也是线程安全的。
  
  优点:能延迟加载,也能保证线程安全。

全部评论

相关推荐

11-09 17:30
门头沟学院 Java
TYUT太摆金星:我也是,好几个华为的社招找我了
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务