一、前言
我们在使用单例模式时,一般有两种选择,一个是懒汉式,一个是饿汉式。但是这两种都是有各自的缺点,无法满足我们的需求,所以DCL(Double Check Lock双端检锁机制)出现了,一种既支持延迟加载、又支持高并发的单例实现方式。 如果不清楚懒汉式为什么不支持高并发可以看一下这篇文章---> 单例模式的懒汉模式为什么在高并发中会出现问题 其他解决方案--->
静态内部类解决单例模式中懒汉式不支持高并发,饿汉式不支持懒加载问题
利用枚举特性解决单例模式中懒汉式不支持高并发,饿汉式不支持懒加载问题
二、具体实现
- DCL单例模式
代码语言:javascript复制import lombok.Data;
import java.util.concurrent.atomic.AtomicInteger;
@Data
public class Phone {
//手机余额
private AtomicInteger count = new AtomicInteger(3);
private Phone(){}
private static Phone phone;
public static Phone getInstance(){
if (phone == null){
synchronized(Phone.class) { // 此处为类级别的锁
if (phone == null) {
phone = new Phone();
}
}
}
return phone;
}
public int getReduce(){
return count.decrementAndGet();
}
}
- 并发测试类
代码语言:javascript复制public class BingFa {
public static void main(String[] args) {
// 创建3个线程,线程里面进行加操作
for (int i = 0; i < 3; i ) {
new Thread(() -> {
Phone phone = Phone.getInstance();
int reduce = phone.getReduce();
System.out.println(Thread.currentThread().getName()
"线程抢到了手机,剩余手机数量" "-----" reduce);
}, String.valueOf(i)).start();
}
}
}
- 测试结果
三、总结
这样就解决了并发问题,懒汉式本来就是懒加载,我们看到三台手机被三个线程抢到了,完美解决问题!!!
附:网上有人说,这种实现方式有些问题。因为指令重排序,可能会导致 Phone对象被 new 出来,并且赋值给 phone之后,还没来得及初始化(执行构造函数中的代码逻辑),就被另一个线程使用了。要解决这个问题,我们需要给 phone成员变量加上 volatile 关键字,禁止指令重排序才行。实际上,只有很低版本的 Java 才会有这个问题。我们现在用的高版本的 Java 已经在 JDK 内部实现中解决了这个问题(解决的方法很简单,只要把对象 new 操作和初始化操作设计为原子操作,就自然能禁止重排序)。
本段话来自极客时间王老师
Q.E.D.