单例模式
学习视频: 23种设计模式,终于有人用一个项目讲清楚了,保姆级教程通俗易懂
1. 最开始的想法
定义类的构造方法为private
不让外界调用,通过这种方式来控制类的创建,从而实现只有一个类的实例。
代码如下:
public class Mgr01 {
private Mgr01() {
System.out.println("===>Mgr05");
};
private static final Mgr01 INSTANCE = new Mgr01();
public static Mgr01 getINSTANCE() {
return INSTANCE;
}
public static void main(String[] args) {
Mgr01 m1 = getINSTANCE();
Mgr01 m2 = getINSTANCE();
System.out.println(m1 == m2);
}
}
但是这里有一个缺陷,类加载的时候就新建了一个实例,不管此时程序用没用到。
2. 需要的时候再new
对象
当INSTANCE
为null
时再初始化实例。
代码如下:
public class Mgr02 {
private Mgr02() {};
private static Mgr02 INSTANCE;
public static Mgr02 getInstance() {
if (INSTANCE == null) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
INSTANCE = new Mgr02();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i=0; i<50; i++) {
new Thread(() -> {
System.out.println(getInstance().hashCode());
}).start();
}
}
}
但是,这里带来了另外一个更严重的问题:线程不安全。
多个线程同时获取实例,有可能创建出多个实例出来。
3. 加锁解决线程不安全问题
代码如下:
public class Mgr03 {
private Mgr03() {};
private static Mgr03 INSTANCE;
public static synchronized Mgr03 getInstance() {
if (INSTANCE == null) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
INSTANCE = new Mgr03();
}
return INSTANCE;
}
}
问题解决,但是还是有缺陷,每次获取实例的时候都会去获取锁,这个过程,减低了程序性能。
4. 优化加锁过程
只有INSTANCE
为null
时,才需要加锁。
代码如下:
public class Mgr04 {
private Mgr04() {};
private static Mgr04 INSTANCE;
public static Mgr04 getInstance() {
if (INSTANCE == null) {
synchronized (Mgr04.class) {
if (INSTANCE == null) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
INSTANCE = new Mgr04();
}
}
}
return INSTANCE;
}
}
注意这里INSTANCE
是否为null
需要判断两次,因为判断INSTANCE
为null
和new
一个实例是属于同一个原子操作内,不能拆分。
5. 静态内部类
利用静态内部类的特性,实现懒加载,并且JVM
的类加载机制保证了线程安全。
代码如下:
public class Mgr05 {
private Mgr05() {
System.out.println("===>Mgr05");
}
private static class Mgr05Holder {
private static final Mgr05 INSTANCE = new Mgr05();
}
public static Mgr05 getInstance() {
return Mgr05Holder.INSTANCE;
}
}
6. 枚举单例
代码如下:
public enum Mgr06 {
INSTANCE;
public static void main(String[] args) {
for (int i=0; i<50; i++) {
new Thread(() -> {
System.out.println(Mgr06.INSTANCE);
}).start();
}
}
}