单例模式:创建一个独一无二,只能有一个实例的对象,并提供全局访问点。
单例模式想必大家都不陌生,面试题出现频率颇高,但是要完全答对却不是那么容易,特别是涉及到多线程的时候,很容易绕晕。
public class Singleton{
//不管有没有用到,先创建实例
private static Singleton singleton = new Singleton();
// 私有构造方法
private Singleton(){}
//需要时直接返回
public static Singleton getInstance(){
return singleton;
}
}
优点:这种写法是在类装载的时候就完成实例化,避免了线程同步问题,是线程安全的。
缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果,如果未使用过这个实例,则会造成内存的浪费。
public class Singleton {
private static Singleton singleton;
// 私有的构造方法
private Singleton(){}
public static Singleton getInstance(){
// 被动创建,在需要时才去创建
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
优点:这种写法起到了Lazy Loading的效果。
缺点:在多线程下,会产生多个实例,是线程不安全的,所以在多线程环境下不可使用这种方式。
public class Singleton {
//多线程中变量需要加上volatile关键字,jdk1.5以上才支持
private volatile static Singleton singleton;
// 私有的构造方法
private Singleton(){}
//直接在方法上加上synchronized关键字
public static synchronized Singleton getInstance(){
// 被动创建,在需要时才去创建
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
这种方法确实可以达到目的,但是直接在方法上加锁,以后不论Singleton是否被创建,只要调用这个方法就会同步,影响性能,所以不建议这样做。
public class Singleton {
//多线程中变量需要加上volatile关键字,jdk1.5以上才支持
private volatile static Singleton singleton;
// 私有的构造方法
private Singleton(){}
public static Singleton getInstance(){
// 被动创建,在需要时才去创建
if (singleton == null) {
//只有创建实例的地方需要加锁
synchronized(Singleton.class){ <-1
singleton = new Singleton(); <-2
}
}
return singleton;
如上所示,将同步锁加在此处虽然解决了每次都要调用同步锁的方法,但是这样做又会造成新的问题,会创建多个实例出来。
假设现在有两个线程(线程1,线程2),当线程1通过 if (singleton == null) 判断进入到<-1位置时,线程2获取了CPU执行权,此时线程1还未进入到同步方法,Singleton实例并没有被创建,所以singleton变量还是为null,线程2也进入到<-1位置,现在不论谁先拿到锁,都会将singleton = new Singleton();代码执行2次,因此会创建两个Singleton实例,所以还得继续改造。
public class Singleton {
//多线程中变量需要加上volatile关键字,jdk1.5以上才支持
private volatile static Singleton singleton;
//私有的构造方法
private Singleton(){}
public static Singleton getInstance(){
//被动创建,在需要时才去创建
if (singleton == null) {
//只有创建实例的地方需要加锁
synchronized(Singleton.class){ <-1
//这里再判断一次是否为null
if (singleton == null) { <-2
singleton = new Singleton(); <-3
}
}
}
return singleton;
在同步代码里再加一层非空判断,即使线程1和线程2都进入到了<-1位置,当其中一个线程创建了Singleton实例后,另一个线程执行到<-2时,就会返回fasle,这样就完美解决了懒汉模式线程不安全问题。