当前位置 : 主页 > 编程语言 > java >

java多线程: volatile与synchronized在单例模式中的使用

来源:互联网 收集:自由互联 发布时间:2022-09-02
文章目录 ​​背景​​ ​​饿汉式加载单例模式在多线程环境中的问题​​ ​​原因分析​​ ​​synchronized同步与volatile修饰​​ ​​总结​​ 背景 在单例模式的设计中,一般会用


文章目录

  • ​​背景​​
  • ​​饿汉式加载单例模式在多线程环境中的问题​​
  • ​​原因分析​​
  • ​​synchronized同步与volatile修饰​​
  • ​​总结​​

背景

在单例模式的设计中,一般会用到两种方式:

立即加载 : 在类加载初始化的时候就主动创建实例;也称为饿汉式加载。
延迟加载 : 等到真正使用的时候才去创建实例,不用时不去主动创建。也称为懒汉式加载。

//饿汉式加载
//立即创建对象
private static SingletonThread singletonThread = new SingletonThread ();

private SingletonThread (){}

public static SingletonThread getInstance(){
return singletonThread ;
}

//懒汉式加载
private static SingletonThread instance;
//使用用才创建对象
public static SingletonThread getInstance() {
if (instance == null) {
instance = new SingletonThread();
}
return instance;
}

饿汉式加载单例模式在多线程环境中的问题

/**
* 懒汉式单例模式在多线程安全的问题
*
* @author zhuhuix
* @date 2020-05-08
*/
public class SingletonThread {

static SingletonThread instance;

static SingletonThread getInstance() {
if (instance == null) {
instance = new SingletonThread();
}
return instance;
}

public static void main(String[] args) {

for (int i = 0; i < 10; i++) {
new Thread(() -> {
SingletonThread singletonThread = SingletonThread.getInstance();
//打印线程获取的hash值,用来判断返回的是否是同一单例
System.out.println(Thread.currentThread().getName() + ":" + singletonThread.hashCode());
}).start();
}

}

}

多次运行以上程序,会出现以下BUG,第一个线程得到的实例的hash值与其他线程的得到的hash不一致,单例模式未生效。

java多线程: volatile与synchronized在单例模式中的使用_单例模式

原因分析
  • 饿汉式加载单例模式在单例类创建时就完成了对象的初始化,在多线程环境下,线程访问单例时对象就创建完成了,所以线程天生就是安全的。
  • 而懒汉式加式模式是使用单例类时才完成对象的初始化,在多线程环境下,多个线程会同时进入到 if (instance == null) { instance = new SingletonThread();}很有可能造成创建出多个实例,违背了单例模式的初衷。
synchronized同步与volatile修饰

那怎么解决以上代码中有可能出现的多个实例的问题呢?可以添加synchronized同步锁与volatile修饰静态变量来解决(synchronized的原理可参见​​ java多线程:synchronized的深度理解​​​)(volatile的原理可参见​​java多线程:volatile的深度理解​​ )

/**
* 懒汉式单例模式在多线程安全的问题
*
* @author zhuhuix
* @date 2020-05-08
*/
public class SingletonThread {

//使用volatile关键字防止重排序,因为 new Instance()是一个非原子操作,可能创建一个 不完整的实例
static volatile SingletonThread instance;

static SingletonThread getInstance() {
if (instance == null) {
//对代码块线程同步锁,进行双重判断
synchronized (SingletonThread.class) {
if (instance == null) {
instance = new SingletonThread();
}
}
//
}
return instance;
}

static void destroy() {
instance = null;
}

public static void main(String[] args) throws InterruptedException {

for (int k = 0; k < 100; k++) {
System.out.println("第" + (k + 1) + "次");
Thread[] threads = new Thread[10];

for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
SingletonThread singletonThread = SingletonThread.getInstance();
//打印线程获取的hash值,用来判断返回的是否是同一单例
System.out.println(Thread.currentThread().getName() + ":" + singletonThread.hashCode());

});

}

for (int i = 0; i < 10; i++) {
threads[i].start();
}

for (int i = 0; i < 10; i++) {
threads[i].join();
}

SingletonThread.destroy();

}
}

}

总结

为保证程序的执行效率 ,考虑只对部分代码块用synchronized实现同步锁,并且用volatile进行修饰静态变量,这种做法无疑是优秀的。


上一篇:Java Web的基础--Servlet
下一篇:没有了
网友评论