由上一篇,了解到懒汉以及饿汉模式,目的是
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
但是,在多线程的情况下,会出现一些问题,如下代码,创建懒汉模式,但是通过同时 new 了两个线程来创建,此时两个线程一起走,但是创建需要延迟 2s 秒钟,所以创建的时候,判断 instance 都是 null,也就是说,两个线程创建了两个不同的对象,如图:
package demo11;
/**
* @Classname LazyMode
* @Description 懒汉模式优化
* @Date 2021/9/24 14:37
* @Created by thx
*/
public class LazyMode {
public static void main(String[] args) {
new Thread(() -> {
TvStation tv = TvStation.getInstance();
System.out.println(Thread.currentThread().getId() + " " + tv);
}).start();
new Thread(() -> {
TvStation tv = TvStation.getInstance();
System.out.println(Thread.currentThread().getId() + " " + tv);
}).start();
}
}
class TvStation {
/**
* 懒汉模式
*/
private static TvStation instance = null;
private TvStation() {
System.out.println("开始创建超市,需要 2s ");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("创建结束");
}
public static TvStation getInstance() {
if (instance == null) {
instance = new TvStation();
}
return instance;
}
}
那么如何解决这一问题呢?
使用 synchronized,然后再判断一次 空,此方法由于判断两次,所以叫 double-check
package demo11;
/**
* @Classname LazyMode
* @Description 懒汉模式优化
* @Date 2021/9/24 14:37
* @Created by thx
*/
public class LazyMode {
public static void main(String[] args) {
new Thread(() -> {
TvStation tv = TvStation.getInstance();
System.out.println(Thread.currentThread().getId() + " " + tv);
}).start();
new Thread(() -> {
TvStation tv = TvStation.getInstance();
System.out.println(Thread.currentThread().getId() + " " + tv);
}).start();
}
}
class TvStation {
/**
* 懒汉模式
*/
private static TvStation instance = null;
private TvStation() {
System.out.println("开始创建超市,需要 2s ");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("创建结束");
}
public static TvStation getInstance() {
// double-check 模式
if (instance == null) {
synchronized (TvStation.class) {
if (instance == null) {
instance = new TvStation();
}
}
}
return instance;
}
}
此问题只在懒汉模式有效,饿汉模式没有这个问题。
枚举天生适应单例属性:
package demo11;
import javax.swing.plaf.TableHeaderUI;
/**
* @Classname EnumMode
* @Description
* @Date 2021/9/24 14:56
* @Created by thx
*/
public class EnumMode {
public static void main(String[] args) {
RadioStation rs1 = RadioStation.CENTER;
RadioStation rs2 = RadioStation.CENTER;
System.out.println(rs1 + " " + rs2);
new Thread(() -> {
RadioStation rs3 = RadioStation.CENTER;
}).start();
new Thread(() -> {
RadioStation rs4 = RadioStation.CENTER;
}).start();
}
}
enum RadioStation {
CENTER;
RadioStation() {
System.out.println("开始创建电视台,需要 2s ");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("创建结束");
}
}