1.实现一个简单单例
饿汉式
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return singleton;
}
}
懒汉式
public class SingletonTest {
private static SingletonTest instance = null;
//静态方法构造 判断是空再加载
public static SingletonTest getInstance() {
if(null == instance) {
instance = new Singleton();
}
return instance;
}
}
但是在多线程环境下会有问题,线程1实例化的时候还没实例化完,线程2判断也是空,就会出现问题,因此我们升级上述模式
加synchronized
public class SingletonTest {
private static SingletonTest instance = null;
//静态方法构造 判断是空再加载
public static synchronized SingletonTest getInstance() {
if(null == instance) {
instance = new Singleton();
}
return instance;
}
}
这种方式解决了初始化实例多线程的问题,却导致了每次只能有一个线程调用该方法,其他线程都会被锁住,性能上会有问题,那么有更好的方式吗?
我们不去锁整个方法呢?
锁对象
public class SingletonTest {
private static SingletonTest instance = null;
//静态方法构造 判断是空再加载
public static SingletonTest getInstance() {
if(instance==null){
synchronized(Singleton.class){
instance = new Singleton();
}
}
return instance;
}
}
该方式也会有问题 ,线程1判断instance == null,暂停。线程2判断instance==null,线程2开始new对象,但是在线程1创建完对象, 线程2执行new 对象。所以还是有问题。
DCL实现
public class SingletonTest {
private static SingletonTest instance = null;
//静态方法构造 判断是空再加载
public static SingletonTest getInstance() {
if(instance==null){
synchronized(Singleton.class){
if(instance==null) {
instance = new Singleton();
}
}
}
return instance;
}
}
只有实例第一次被访问时,才会有线程进入同步块,这样极大提高了性能。避免了synchronized带来的较大性能损失。
第一次访问时,如果有多个线程同时进入if块,只有第一个线程会获得锁,其他线程被阻塞,第一个线程可以创建实例。
第一次访问时,被阻塞的线程会进入同步块,进行第二次check,如果此时实例不为null,则返回。
说到这里,不得不提到指令重排。那么一个实例初始化得过程是怎么样的呢
1 分配内存
2 初始化对象
3 设置instance指向刚分配的地址
发生指令重排可能会改变123 的顺序变成132 这时候 对象可能还没有初始化完就已经实例化了。
volatile 关键字有可见性,禁止指令重排的功能。所以距离线程安全就差这一步了。
DCL线程安全实现–volatile实现
public class SingletonTest {
private static volatile SingletonTest instance = null;
//静态方法构造 判断是空再加载
public static SingletonTest getInstance() {
if(instance==null){
synchronized(Singleton.class){
if(instance==null) {
instance = new Singleton();
}
}
}
return instance;
}
}
好了,至此我们实现dcl线程安全的单例模式