JAVA实现单例模式主要有:懒汉、饿汉、双重检验锁、静态内部类和枚举几种方式。
一、懒汉,线程不安全
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
这种方式之所以线程不安全是因为当多个线程同时使用getInstance()方法时,可能产生多个实例。
二、懒汉,线程安全
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
加上synchronized关键字后,保证了线程安全,但是任何时候只能有一个线程调用getInstance()方法,所以并不高效。
三、饿汉
public class Singleton{ //类加载时就初始化 private static final Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }
这种方式使得类在装载时就创建了实例。
四、双重检验锁
public class Singleton { private volatile static Singleton instance; //声明成 volatile private Singleton (){} public static Singleton getSingleton() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
这里有两点要注意,一是两次检验instance==null,因为可能有多个线程同时进入外面的if,如果没有内部if检查,会创建多个实例。二是volatile关键字。因为instance=new Singleton()分三步进行:
1) 给instance分配内存;
2)调用Singleton构造函数初始化成员变量;
3)将instance对象指向内存空间。
由于JVM编译器中存在指令排序的优化,也就是说2)和3)的顺序是不能保证的。设想一下,如果执行顺序是1,3,2,在执行完3之后,然后被线程二占用,这时instance已经是非null,线程二直接返回instance,这样在使用时就会报错。
volatile的作用在于禁止指令重排序优化。
五、静态内部类
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()方法之后。
六、枚举
public enum EasySingleton{ INSTANCE; }
可见枚举类型是最简单的一种方法。