单例模式
确保一个类只有一个实例,并提供一个全局的访问点
最简单的形式
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Singleton1 { private static Singleton1 uniqueInstance; private Singleton1() { } public static Singleton1 getInstance(){ if(uniqueInstance==null){ uniqueInstance=new Singleton1(); } return uniqueInstance; } }
|
上面的代码很容易理解,将构造函数私有化,在静态方法getInstance中,如果不调用getInstance方法,那么这个对象永远不会产生,这是一个延迟实例化
在多线程状态下的问题
因为getInstance方法中的代码不是原子的。如果多个线程同时执行,则有可能产生多个实例。具体是怎样产生的看下图
使用同步处理多线程产生多个实例的问题
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Singleton2 { private static Singleton2 uniqueInstance; private Singleton2() { } public static synchronized Singleton2 getInstance(){ if(uniqueInstance==null){ uniqueInstance=new Singleton2(); } return uniqueInstance; } }
|
只需要在getInstance方法声明中添加 synchronized 关键字就可以解决问题,保证在多线程情况下,同时只有一个线程能够进入这个方法
但是同步会降低性能,每次调用getInstance方法的时候,都需要同步,但是我们在获取到第一个实例之后,后面都不需要同步。在方法声明上设置同步是一种累赘
改善方法同步的问题
如果引用性能问题不是很关键那么什么都别做
使用饿汉式创建单例模式
1 2 3 4 5 6 7 8 9 10
| public class Singleton3 { private static Singleton3 uniqueInstance =new Singleton3(); private Singleton3() { } public static Singleton3 getInstance(){ return uniqueInstance; } }
|
使用饿汉式创建单例,JVM在加载这个类时间,马上创建唯一的单例实例,保证在任何线程访问uniqueInstance静态变量之前,一定要先创建此实例。
懒汉式双重检查锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Singleton4 { private volatile static Singleton4 uniqueInstance; private Singleton4() { } public static Singleton4 getInstance(){ if(uniqueInstance==null){ synchronized (Singleton4.class) { if (uniqueInstance==null) { uniqueInstance = new Singleton4(); } } } return uniqueInstance; } }
|
利用双重检查锁,首先检查实例是否已经创建,,如果尚未创建,才进行同步,这样一来,只会有第一次会同步,如果性能是你关心的重点,那么这个做法可以帮你打打减少getInstance的时间耗费
注意 这里使用了 volatile
保证在多线程的情况下,对修饰的对象的内存可见性。