单例模式中的多线程问题
DCL:(Double Check Lock),双重判断锁, 要知道DCL的由来,先从单例模式说起
单例模式——饿汉式
public class Singleton01 {
private static final Singleton01 INSTANCE = new Singleton01();
private Singleton01 (){
}
public static Singleton01 getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
Singleton01 instance1 = getInstance();
Singleton01 instance2 = getInstance();
System.out.println(instance1 == instance2);
}
}
饿汉式就是甭管三七二十一,我上来就先把这个对象new出来,构造方法是private的,别人创建不了,通过一个静态的getInstance()方法获取实例,这样能够保证我每次获取的对象都是同一个,这是单例最简单的写法。
这时候有人说了,你这写的不咋地啊,我还没用你这个对象呢,你就给我new出来了,太浪费空间了。
单例模式——懒汉式
/**
* 单例模式--懒汉式
*/
public class Singleton02 {
private static Singleton02 INSTANCE;
private Singleton02(){
}
public static Singleton02 getInstance(){
if (INSTANCE == null) {
try {
// 业务代码
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton02();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> System.out.println(Singleton02.getInstance().hashCode())).start();
}
}
}
饱汉式是什么时候使用,我什么时候再给你创建,对象不为空我就直接返回。
这时候又有人说了,你这不对呀,在多线程的情况下是有可能创建多个对象的,什么意思呢?比如有两个线程,第一个线程进来判断INSTANCE是否为空,第一次进来肯定为空,继续执行,在执行业务代码还没有new对象的时候,第二个线程进来,判断INSTANCE是否为空,依然为空,继续往下执行,这时候线程一new了一个对象返回,线程二也继续执行new了一个对象返回,一共创建了两个对象,从上面的程序也能验证出来,hashCode不一致说明不是同一个对象。
单例模式——升级版
/**
* 单例模式--升级版
*/
public class Singleton03 {
private static Singleton03 INSTANCE;
private Singleton03(){
}
public static synchronized Singleton03 getInstance(){
if (INSTANCE == null) {
try {
// 业务代码
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton03();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> System.out.println(Singleton03.getInstance().hashCode())).start();
}
}
}
我在方法上加了一把锁,每个线程进来必须先拿到这把锁才能执行里面的代码,同一时刻,只能有一个线程在执行,执行完的时候一定能new出一个对象,释放锁后,第二个线程才能进来,判断的时候这个对象一定不为空,在多线程的情况下能够保证对象时唯一的。
这时候又有人问了,你这锁加到方法上了,锁的粒度太大了,我只想在需要加锁的地方加锁行不行。
public class Singleton04 {
private static Singleton04 INSTANCE;
private Singleton04(){
}
public static Singleton04 getInstance(){
// 业务代码
if (INSTANCE == null) {
synchronized (Singleton04.class) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton04();
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> System.out.println(Singleton04.getInstance().hashCode())).start();
}
}
}
看下上面这段代码,先判断对象是否为空,为空你再上锁,这个能解决多线程下的数据不一致性问题吗?不能
很显然,还是不能保证多线程下的数据不一致性问题,怎么回事呢?
当线程一进入方法判断对象是否为空,为空,然后第一个线程停了,第二线程进来判断是否为空,为空,继续执行,new了一个对象后返回释放锁,线程一拿到锁后继续执行,又new了一个对象。所以终于诞生了美团问的这个写法,DCL。
单例模式——DCL(DOUBLE CHECK LOCK)
public class Singleton05 {
private static volatile Singleton05 INSTANCE;
private Singleton05(){
}
public static Singleton05 getInstance(){
// 业务代码
if (INSTANCE == null) {
synchronized (Singleton05.class) {
if (INSTANCE == null) { // 双重验证
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton05();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> System.out.println(Singleton05.getInstance().hashCode())).start();
}
}
}
以上这段代码就是DCL

浙公网安备 33010602011771号