单例模式的线程安全性问题浅析

时间:2022-03-11 13:11:07

单例的线程安全问题关注

最近面试工作时经常会被问到单例会带来什么问题?单例是否是安全的?我想如果是单线程就可以放心大胆的说是的,但是

如果多个线程同时调用这个实例,就会有线程安全的问题

 

单例一般用在什么地方?

单例的目的是为了保证运行时只有唯一的一个实例,最常用的地方比如拿到数据库的连接,或者Spring的中创建BeanFactory操作,而这些操作都是调用他们的方法来执行某个特定的动作。

 

首先先来认识下两种模式:  恶汉式  懒汉式

 

单例模式的线程安全性问题浅析
public class MyFactory {

// //饿汉式 立即创建
// private static MyFactory instance = new MyFactory();
//    private MyFactory(){};
// public static MyFactory getInstance(){
// return instance;
// }

//懒汉式用到的时候在创建,不用就不创建(有线程安全问题)
private static MyFactory instance = null;
    private MyFactory(){};
public static MyFactory getInstance(){
     instance = new MyFactory();
return instance;
}
}
单例模式的线程安全性问题浅析

 

 

下面总结一下解决线程安全的几种方式:

方法一:在MyFactory 中加入了一个私有静态内部类instanceHolder ,对外提供的接口是 getInstance()方法,也就是只有在MyFactory.getInstance()的时候,instance对象才会被创建,,没有使用同步。保证了只有一个实例,还同时具有了Lazy的特性

单例模式的线程安全性问题浅析
public class MyFactory {
private static class instanceHolder {
public static MyFactory instance = new MyFactory();
}
private MyFactory(){};
public static MyFactory getInstance() {
return MyFactory.instanceHolder.instance;
}
}
单例模式的线程安全性问题浅析

测试代码

单例模式的线程安全性问题浅析
import static org.junit.Assert.*;
import org.junit.Test;
public class MyFactoryTest {
@Test
public void testGetResource() {
MyFactory mf1
= MyFactory.getInstance();
MyFactory mf2
= MyFactory.getInstance();
System.out.println(mf1
!= null);//true
System.out.println(mf1 == mf2);//true
}
}
单例模式的线程安全性问题浅析

方式二:(懒汉式)

这种方式也没有使用同步,并且确保了调用static getInstance()方法时才创建MyFactory的引用,

  private static MyFactory instance = new MyFactory();
  private MyFactory(){};
public static MyFactory getInstance() {
  
return instance;
}

测试代码:同方法一

 

方式三:使用synchronized 通常会锁定整个方法的是比较耗费资源的,实际会产生多线程访问问题的是这一句代码instance = new MyFactory();

为了减少资源的消耗,只锁这一句就行了, 两个线程并发地进入第一次判断instance是否为空的if 语句内部,一个线程获得了锁执行new操作,另一个线程被阻塞,

当第一个线程执行完毕之后,第二个线程如果直接进行new操作也是不安全的。为了避免第二次new操作,添加第二次条件判断,既二次检查

单例模式的线程安全性问题浅析
    private static MyFactory instance;
    private MyFactory(){};
public static MyFactory getInstance(){
if(instance == null){
synchronized (MyFactory.class) {
if(instance == null){
instance
= new MyFactory();
}
}
}
return instance;
}
单例模式的线程安全性问题浅析

测试代码 同方法一