介绍
有时候允许自由创建某个类的对象没有意义,还可能会造成系统性能下降。如果让一个类只能创建一个实例,这种设计模式被称为单例设计模式。其基本思路就是:定义一个静态变量来缓存该类的实例;私有化构造器;然后对外暴露一个静态方法来获取该类的实例。单例设计模式根据初始化对象的时机不同又分为饿汉式和懒汉式。
饿汉式
饿汉式非常简单,直接贴代码:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
懒汉式
懒汉式是在调用getInstance方法时进行判断,如果对象已经实例化,则直接返回,如果没有实例化,先实例化一个对象,再返回。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
对多线程比较敏感的程序员可能已经意识到了,这样的方式是存在线程安全问题的,并且初始化对象所需的时间越久,越有可能出现线程安全问题,你如果不信,我们可以做一个实验,在构造器里sleep两毫秒来模拟延迟,然后对getInstance方法做并发测试。
private Singleton() {
try {
Thread.sleep(2);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
Thread th = new Thread(() -> {
Singleton instance = Singleton.getInstance();
System.out.println(instance);
});
Thread th1 = new Thread(() -> {
Singleton instance = Singleton.getInstance();
System.out.println(instance);
});
Thread th2 = new Thread(() -> {
Singleton instance = Singleton.getInstance();
System.out.println(instance);
});
th.start();
th1.start();
th2.start();
}
输出的结果为:
thread.Singleton@6c7d6e81
thread.Singleton@5c6ef4da
thread.Singleton@e50a5a2
不难发现这几个线程获取到的对象的内存地址并不相同,说明他们不是同一个对象。所以getInstance方法要加锁。关于多线程相关知识,有兴趣的小伙伴们可以看下我的另外一篇博客——Java多线程基础知识总结
public static Singleton getInstance() {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
再执行一下刚才的并发测试,发现打印的地址都是相同的。这样每个线程在进入getInstance方法后,都会先获取锁,没拿到锁的线程会阻塞,直到拿到锁才能执行。很显然,在高并发的情况下让每个线程都去竞争锁不太高效,比较高效的做法是:先判断一下,如果已经实例化了就直接返回,就不用去竞争锁了,代码如下:
public static Singleton getInstance() {
if (instance != null) {
return instance;
}
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
这种写法叫DCL,即Double Checked Locking.
还有一种更极端的情况,就是“指令重排”问题。这里不打算介绍“指令重排”,看客大佬们可以自己去搜下。所以为了代码的极致严谨,给我们的instance属性加上volatile修饰,至此,懒汉模式的完整代码如下:
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance != null) {
return instance;
}
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}