Java 中的单例模式
是一种创建型设计模式,旨在确保一个类在整个应用程序的生命周期中只有一个实例。
在 Java 中,实现单例模式有多种方式,包括懒汉式、饿汉式、静态内部类、枚举和双重校验锁等。每种方式都有其优势和适用场景。
饿汉式
静态成员变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class Singleton {
private Singleton() {}
private static Singleton instance = new Singleton();
public static Singleton getInstance() { return instance; } }
|
客户端测试
1 2 3 4 5 6 7 8 9 10
| public class Client {
public static void main(String[] args) { Singleton instance = Singleton.getInstance(); Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1); } }
|
总结:
静态代码块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class Singleton {
private Singleton() {}
private static Singleton instance;
static { instance = new Singleton(); }
public static Singleton getInstance() { return instance; } }
|
客户端测试
1 2 3 4 5 6 7 8 9
| public class Client {
public static void main(String[] args) { Singleton instance = Singleton.getInstance(); Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1); } }
|
总结:
枚举类
枚举类型属于饿汉式模式
枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所有单例实现中唯一一种不会被破坏的单例实现模式。其他方式都有可能被反射、序列化方式等破坏。
1 2 3 4 5
| public enum Singleton {
INSTANCE; }
|
客户端测试
1 2 3 4 5 6 7 8 9
| public class Client {
public static void main(String[] args) { Singleton instance = Singleton.INSTANCE; Singleton instance1 = Singleton.INSTANCE;
System.out.println(instance == instance1); } }
|
懒汉式
synchronized 方法加锁(线程安全)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class Singleton {
private Singleton() {}
private static Singleton instance;
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
|
客户端测试
1 2 3 4 5 6 7 8 9
| public class Client {
public static void main(String[] args) { Singleton instance = Singleton.getInstance(); Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1); } }
|
总结:
- 缺点:synchronized 加在方法上,性能较差。
双重检查锁(线程安全)
双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是 JVM 在实例化对象的时候会进行优化和指令重排操作。要解决双重检查锁模式带来的空指针异常的问题,只需要使用 volatile
关键字,volatile
关键字可以保证可见性和有序性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
public class Singleton {
private Singleton() {}
private static volatile Singleton instance;
public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } }
return instance; } }
|
客户端测试
1 2 3 4 5 6 7 8 9
| public class Client {
public static void main(String[] args) { Singleton instance = Singleton.getInstance(); Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1); } }
|
静态内部类(线程安全)
静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。
说明: 第一次加载 Singleton 类时不会去初始化 INSTANCE,只有第一次调用 getInstance,虚拟机加载 SingletonHolder 并初始化 INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
public class Singleton {
private Singleton() {}
private static class SingletonHolder { private static final Singleton instance = new Singleton(); }
public static Singleton getInstance() { return SingletonHolder.instance; } }
|
客户端测试
1 2 3 4 5 6 7 8 9
| public class Client {
public static void main(String[] args) { Singleton instance = Singleton.getInstance(); Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1); } }
|