先来个单例模式压压惊,单例模式也是面试常考的哦。
饿汉式(jvm保证线程安全)
/*
饿汉式由jvm来保证一个类的<cintit>方法在多线程环境下被正确地加锁同步,如果多个线程同时去初始化一个类,
那么只会有其中一个线程去执行这个类的<cinit>方法,其他线程都需要阻塞等待,直到活动线程执行完毕。
如果一个类的<cinit>方法执行耗时太长,可能造成多个线程阻塞等待。
*/
public class SingtonHungry {
public SingtonHungry() {
}
private static SingtonHungry instance=new SingtonHungry();
public static SingtonHungry getInstance(){
return instance;
}
}
懒汉式1(非改进版,存在线程安全问题)
/*
如果abc三个线程同时调用静态的getInstance(),
会造成多个线程获得不同的instance实例(分配的内存地址不同),
这就有违单例模式了,所以这是有问题的。
*/
public class SingletonLazy {
private static SingletonLazy instance=null;
private static SingletonLazy getInstance(){
if(instance==null){
instance=new SingletonLazy();
}
return instance;
}
}
懒汉式2(改进版,线程安全,但吞吐量太低)
/*
使用synchronized锁来保护临界区共享资源
*/
public class SingletonLazy {
private static SingletonLazy instance=null;
private static SingletonLazy getInstance(){
synchronized (SingletonLazy.class) {
/*这里是线程安全的,因为每次进来都需要加锁。
打个比方,ab线程同时进来,a线程进入monitorEnter的逻辑,b线程进入entryList阻塞队列去等待锁释放,
a线程初始化完instance后,进入monitorExit逻辑,释放锁。b线程还需要再次判断instance是否为null。
*/
if(instance==null) {
instance = new SingletonLazy();
}
}
//缺点就是,不管instance是否初始化完成,后续的每个线程进来获取instance实例都需要加锁,效率太低。
return instance;
}
}
懒汉式3(双重检查锁,存在线程安全问题,因为现代cpu多级指令流水线提高吞吐量的同时,可能会导致指令重排序,需要大量压测才能测试出来这个问题)
/*
双重检查锁
*/
public class SingletonLazy {
public SingletonLazy() {
}
private static SingletonLazy instance=null;
private static SingletonLazy getInstance(){
/*
这样看似已经做到线程安全了,并且后续线程进来只需判断instance是否为null,发现不为null直接返回即可。
但还是存在问题的实际上。
*/
/*
分析:第一重检查的instance==null的逻辑判断是没有加锁的,并且也没有使用volatile关键字来修饰。所以极端情况:
a、b两个线程