在线程安全方面,上面的饿汉模式是在多线程下是安全的,而懒汉模式在多线程下是不安全的;
因为,如果多个线程同时访问一个变量,那么不会出现不安全问题,如果多个线程同时修改一个变量,就有可能出现不安全问题;
饿汉模式下,只进行了访问,没有涉及到修改
懒汉模式下,不仅进行了访问,还涉及了修改,那么下面就讲解以下懒汉模式在多线程下如何会产生不安全
既然出现了不安全问题,那么如何将懒汉模式修改成安全的呢?
????方法:进行加锁,使线程安全
但是,如果锁加在这个地方,仍然是不安全的,因为,这样还是会进行穿插执行,如果两个并发的进入的 if 语句中,那么,就会进行锁竞争,假设,thread1 获取到了锁,thread2 在阻塞等待,等到 thread1 创建一次对象,释放锁后,thread2 就又会载获取到锁,进行创建对象,所以,这个加锁操作并没有保证它是一个整体(非原子性)
所以说,并不是加了锁就安全,只有锁加对了才会安全,在加锁的时候要保证以下几方面:
-
锁的 {} 的范围是合理的,能够把需要作为整体的每个部分都包括进去;
-
锁的对象能够起到锁竞争的效果;
懒汉模式多线程版改进????:
将if语句和new都放在锁里面成为一个整体,这样就避免了会穿插执行;
public static SingleTon getInstance() {
synchronized (SingleTon.class) {
if(instance == null) {
instance = new SingleTon();
}
}
return instance;
}
但是上述代码还有一个问题,每当调用getInstance时,都会尝试去进行加锁,而加锁是一个开销很大的操作,而懒汉模式之所以会出现线程不安全问题,是因为只是在第一次调用getInstance方法new对象时,可能会出现问题,但是,只要new完对象以后,就不用再进行锁竞争了,直接访问就可以了,所以再次进行优化????:
public static SingleTon getInstance() {
//在最外面在进行一次判断
if(instance == null) {
synchronized (SingleTon.class) {
if(instance == null) {
instance = new SingleTon();
}
}
}
return instance;
}
在第一次实例化对象后,以后再调用个getInstance方法时,就不会再创建对象,而且也不会再去获取锁,因为,第一个if判断语句都不会进去,所以不会执行到加锁的语句;
上面的单例模式看着好像是完全没问题了,但是,还是有一个问题,就是可能会触发指令重排序问题,所以就需要使用volatile解决指令重排序问题: