>>本文转自“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来保证。
喜欢的话可以打赏一下哦!!!

支付宝

微信