设计模式专辑——单例的懒汉模式和饿汉模式

>>本文转自“Java知音“

 

1.懒汉模式

代码1
//懒汉式单例模式
//在类加载时,不创建实例,因此类加载速度快,但运行时获取对象的速度慢
public
class LazySingleton{ private static LazySingleton instance = null; private LazySingleton(){} public static synchronized LazySingleton getInstance(){ if(instance == null){ instance = new LazySingleton(); } return instance; } }

代码1中在getInstance上加同步锁的方式,十分影响效率。对此,出现了如下双重检查的方式

代码2
public
class LazySingleton{ private static LazySingleton instance = null; private LazySingleton(){} public static LazySingleton getInstance(){ if(instance == null){ synchronized(LazySingleton.class){ if(instance==null){ instance = new LazySingleton(); } } } return instance; } }

使用如代码2的双重检查模式,使得只在instance为null时才需要加同步锁,从而提高了效率。

但是代码2存在一个细微的问题,即instance = new LazySingleton()并不是一个原子操作,它包括以下三步原子操作:

1.为实例对象分配内存

2.对象初始化

3.instance指向分配的内存

在指令重排的情况下,第3步可能会在第2步之前执行。

此时在其他线程看来instance已经非null,导致getInstance方法返回未初始化完成的instance对象。

为了解决这个问题,需要引入volatile关键字,代码如下

代码3
public class LazySingleton{
  private static volatile LazySingleton instance = null;

  private LazySingleton(){}

  public static LazySingleton getInstance(){
    if(instance == null){
      synchronized(LazySingleton.class){
        if(instance==null){
           instance = new LazySingleton();
        }
      } 
    }
    return instance;
  }      
}

volatile能保证可见性和有序性。

在这里volatile保证了有序性。

volatile关键字禁止指令重排序有两层意思:

1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;

2)在进行指令优化时,不能将在对volatile变量的读操作或者写操作的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行

 

2.饿汉模式

代码4
//饿汉单例模式
//类加载较慢,但获取对象的速度快
//且线程安全
public
class EagerSingleton{ private static EagerSingleton instance = new EagerSingleton(); private EagerSingleton(){ ... } public static EagerSingleton getInstance(){ return instance; } }

饿汉模式线程安全,只不过在类加载时就进行了实例初始化,可能有点浪费。 

 

3.推荐方式

 综上,考虑到延迟加载和线程安全,推荐使用以下方式

代码5
//静态内部类形式
public
class Singleton{ private static class SingletonHolder{ private static final Singleton INSTANCE = new Singleton(); } private Singleton(){} public static final Singleton getInstance(){ return SingletonHolder.INSTANCE; } }

这样初始化延迟到getInstance方法被调用时,并且同步由ClassLoader来保证。

 

喜欢的话可以打赏一下哦!!!

支付宝

微信

posted @ 2018-04-11 09:29  PaganMonkey  阅读(230)  评论(0编辑  收藏  举报

喜欢的话可以打赏一下哦!!!

支付宝

微信