单例设计模式线程安全问题
懒汉式
class Lazy {
private Lazy(){
System.out.println("test Lazy");
}
private static Lazy lazy=null;
public static Lazy getInstance(){
if(lazy==null){
lazy=new Lazy();
}
return lazy;
}
}
懒汉式先声明一个private对象,对象为空,静态方法中判断是否为空,如果为空,则new一个对象给声明的private对象,并返回该对象。
分析:
单线程:此状态下是安全的,第一次调用静态方法之后,“lazy”就不为空了,第二次调用静态方法的时候,判断"lazy"不为空,返回该对象,和第一次调用静态方法产生的对象是同一个。
多线程:此状态下是不安全的,若多个线程一开始同时进入静态方法中,对"lazy"进行判断,全部线程都判断为空,那么都会创造一个对象,那么就破坏了单例。
改进方案:
class Lazy {
private static volatile Lazy instance = null;
private Lazy() {
System.out.println("test Lazy");
}
public static Lazy getInstance() {
if (instance == null) {
synchronized(Lazy.class){
if(instance==null)
instance=new Lazy();
}
}
return instance;
}
}
分析: 声明私有对象的时候加上了volatile,为了保证 "instance=new Lazy();" 这一句不会被指令重排,保证了其原子性,其次用了synchronized来加锁,当初次多个线程进来时,开始时判断 "instance" 为空,多个线程进入,然后只有一个线程获得锁,该线程进去之后再次判断是否为空,为空,new一个对象赋给 "instance",释放锁,然后第二个抢到锁的,进去发现 "instance" 已经不为空了,释放锁,所有的线程都出来之后,返回 "instance"。
饿汉式
class Hungry {
private Hungry(){
System.out.println("test Hungry");
}
private static Hungry hungry=new Hungry();
public static Hungry get(){
return hungry3;
}
}
分析: 因为实例声明为static,且被实例化,在类加载的时候就已经被加载完成,还未使用该类的时候就已经创建好了实例,而且只有一个实例,就算多个线程进来也无法创建新的实例,所以多线程时是安全的,但是,未使用的时候就创建了实例,会浪费空间,造成内存不必要的消耗。
枚举类型
enum EnumTest{
INSTANCE;
public EnumTest getInstance(){
return INSTANCE;
}
}