1.线程安全的单例模式
场景引入:
什么是线程安全?如何判断一个方法是线程安全的?方法只要加了锁就是线程安全的吗?
当多个线程访问某个方法时,不管你通过怎样的调用方式或者说这些线程如何交替的执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。
并不是只要加了锁就是安全的,只有满足了那把锁的对象是锁同一个对象才是线程安全的。比如synchronized关键字中:
- synchronized修饰非static方法等同于synchronized(this){};
- synchronized修饰static方法等同于synchronized(xxx.class);
并发编程三要素(线程的安全性问题体现在):
- 原子性:原子,即一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。
- 可见性:一个线程对共享变量的修改,另一个线程能够立刻看到。(synchronized,volatile)
- 有序性:程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)
出现线程安全问题的原因:
-
线程切换带来的原子性问题
-
缓存导致的可见性问题
-
编译优化带来的有序性问题
解决办法:
- JDK Atomic开头的原子类、synchronized、LOCK,可以解决原子性问题
- synchronized、volatile、LOCK,可以解决可见性问题
- Happens-Before 规则可以解决有序性问题
class Singleton{
private volatile static Singleton uniqueInstance;
private Singleton(){
}
public Singleton getUniqueInstance(){
if(uniqueInstance == null) {
synchronized (Singleton.class) {
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
问:为什么要两次判断
uniqueInstance == null
?
- 第一次判断对象是否已经创建实例化过,没有实例化过才进入加锁代码,否则直接返回实例(提高效率)
- 第二次的话,如果没有没这判断,假设两个线程t1、t2同时经过了第一次判断,进入了6、7行之间,这时