单例模式:确保一个类只有一个实例,并提供一个全局访问点。
代码实现:
package com.headfirst.chapter5; public class Singleton { private static Singleton singleton; private Singleton(){} public static Singleton getInstance(){ if(singleton == null){ singleton = new Singleton(); } return singleton; } }
上面这个例子在高并发情况下,很可能会产生两个实例。
即可能会有一个线程进入singleton = new Singleton()并且还未实例化,
另一个线程在singleton == null 为true。这样就会产生两个实例
将上面的getInstance方法加一个同步块。如下
package com.headfirst.chapter5; public class Singleton { private static Singleton singleton; private Singleton(){} public static synchronized Singleton getInstance(){ if(singleton == null){ singleton = new Singleton(); } return singleton; } }
这样就能保证只生成一个实例,但在高并发情况下,这个方法会有性能问题。
继续修改,给singleton变量加上volatile修饰
并且只有在第一次实例化时,才会进同步块,这样就解决了同步的性能问题。
package com.headfirst.chapter5; public class Singleton { private volatile static Singleton singleton; private Singleton(){} public static Singleton getInstance(){ if(singleton == null){ synchronized(Singleton.class){ if(singleton == null){ singleton = new Singleton(); } } } return singleton; }
为什么不把singleton定义成全局的静态变量,如下
public static Singleton singleton = new Singleton();
如果这样写,那么在Singleton初始化的时候,这个变量就会被实例化,即急切实例化。
而不像上面的例子,只有在真正使用到这个变量的时候,才去实例化它。
Volatile 变量具有 synchronized
的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。
而volatile使用时有明确的规定:
- 对变量的写操作不依赖于当前值;
- 该变量没有包含在具有其他变量的不变式中;