线程安全的单例模式(Singleton)。
在Java中,实现线程安全的单例模式(Singleton)通常涉及确保类的实例在多线程环境中只被创建一次。有多种方法可以实现这一点,包括使用synchronized
关键字、双重检查锁定(Double-Checked Locking, DCL)、静态内部类(Bill Pugh Singleton Design)以及使用java.util.concurrent
包中的类。
以下是几种常见的线程安全单例模式实现:
1. 饿汉式(Eager Initialization)
饿汉式在类加载时就完成了实例的创建,因此是线程安全的。
public class Singleton { | |
private static final Singleton INSTANCE = new Singleton(); | |
// 私有构造函数防止实例化 | |
private Singleton() {} | |
public static Singleton getInstance() { | |
return INSTANCE; | |
} | |
} |
2. 懒汉式(Lazy Initialization),使用synchronized
方法
这种方法在需要时才创建实例,但整个getInstance
方法是同步的,可能会影响性能。
public class Singleton { | |
private static Singleton instance; | |
// 私有构造函数防止实例化 | |
private Singleton() {} | |
public static synchronized Singleton getInstance() { | |
if (instance == null) { | |
instance = new Singleton(); | |
} | |
return instance; | |
} | |
} |
3. 双重检查锁定(Double-Checked Locking)
这种方法结合了懒汉式的延迟初始化和饿汉式的性能优势,通过减少同步代码块的范围来提高性能。
public class Singleton { | |
private static volatile Singleton instance; | |
// 私有构造函数防止实例化 | |
private Singleton() {} | |
public static Singleton getInstance() { | |
if (instance == null) { // 第一次检查,无需同步 | |
synchronized (Singleton.class) { | |
if (instance == null) { // 第二次检查,需要同步 | |
instance = new Singleton(); | |
} | |
} | |
} | |
return instance; | |
} | |
} |
注意:这里使用了volatile
关键字来确保instance
变量的可见性,并禁止指令重排序。
4. 静态内部类(Bill Pugh Singleton Design)
这种方法利用了类加载机制来保证线程安全和延迟加载。
public class Singleton { | |
private Singleton() {} | |
private static class SingletonHelper { | |
private static final Singleton INSTANCE = new Singleton(); | |
} | |
public static Singleton getInstance() { | |
return SingletonHelper.INSTANCE; | |
} | |
} |
5. 使用java.util.concurrent.locks.Lock
这种方法使用Java并发包中的Lock
接口来提供显式的锁控制。
import java.util.concurrent.locks.Lock; | |
import java.util.concurrent.locks.ReentrantLock; | |
public class Singleton { | |
private static Singleton instance; | |
private static final Lock lock = new ReentrantLock(); | |
// 私有构造函数防止实例化 | |
private Singleton() {} | |
public static Singleton getInstance() { | |
lock.lock(); | |
try { | |
if (instance == null) { | |
instance = new Singleton(); | |
} | |
} finally { | |
lock.unlock(); | |
} | |
return instance; | |
} | |
} |
6. 使用java.util.concurrent.atomic.AtomicReference
这种方法利用了原子变量来确保线程安全的延迟初始化。
import java.util.concurrent.atomic.AtomicReference; | |
public class Singleton { | |
private static final AtomicReference<Singleton> INSTANCE_REF = new AtomicReference<>(); | |
// 私有构造函数防止实例化 | |
private Singleton() {} | |
public static Singleton getInstance() { | |
Singleton instance = INSTANCE_REF.get(); | |
if (instance == null) { | |
synchronized (Singleton.class) { | |
instance = INSTANCE_REF.get(); | |
if (instance == null) { | |
instance = new Singleton(); | |
INSTANCE_REF.set(instance); | |
} | |
} | |
} | |
return instance; | |
} | |
} |
然而,这种方法实际上是双重检查锁定的一个变种,但它使用了AtomicReference
来封装实例变量。在实际应用中,直接使用静态内部类或双重检查锁定通常更为简洁和高效。
总结
对于大多数应用场景,静态内部类方法(Bill Pugh Singleton Design)是首选,因为它既简单又高效,同时保证了线程安全和延迟加载。双重检查锁定也是一个不错的选择,但需要小心处理volatile
关键字和指令重排序的问题。