单例模式是设计模式中最基础也最常用的模式之一,它的核心在于确保一个类只有一个实例,并提供一个全局访问点。在实际开发中,单例模式的应用场景非常广泛:
我曾在多个大型项目中负责核心模块开发,发现单例模式的正确使用能显著提升系统稳定性。特别是在高并发场景下,一个设计良好的单例可以避免资源竞争和数据不一致问题。
java复制public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return instance;
}
}
优点:
缺点:
java复制public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
优点:
缺点:
java复制public class DCLSingleton {
private volatile static DCLSingleton instance;
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if (instance == null) {
synchronized (DCLSingleton.class) {
if (instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}
优点:
缺点:
java复制public class HolderSingleton {
private HolderSingleton() {}
private static class Holder {
private static final HolderSingleton INSTANCE = new HolderSingleton();
}
public static HolderSingleton getInstance() {
return Holder.INSTANCE;
}
}
这种实现方式结合了饿汉式的线程安全和懒汉式的延迟加载优势:
java复制public enum EnumSingleton {
INSTANCE;
public void doSomething() {
// 业务方法
}
}
枚举单例是《Effective Java》作者Joshua Bloch推荐的方式:
对于需要管理多个单例对象的场景,可以使用Map来维护引用:
java复制public class SingletonManager {
private static Map<String, Object> instanceMap = new ConcurrentHashMap<>();
private SingletonManager() {}
public static Object getInstance(String className) {
synchronized (instanceMap) {
if (!instanceMap.containsKey(className)) {
try {
Object instance = Class.forName(className).newInstance();
instanceMap.put(className, instance);
} catch (Exception e) {
e.printStackTrace();
}
}
return instanceMap.get(className);
}
}
}
这种方式适合需要统一管理多个单例的场景,比如插件系统。
在现代Java开发中,我们经常使用Spring等框架。如何让框架管理的单例与传统单例协同工作?
java复制@Component
public class AppConfig {
@Bean
@Scope("singleton")
public SomeService someService() {
return SomeServiceImpl.getInstance();
}
}
关键点:
当单例类实现Serializable接口时,反序列化会破坏单例特性。解决方案:
java复制public class SerializableSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private SerializableSingleton() {}
private static class Holder {
private static final SerializableSingleton INSTANCE = new SerializableSingleton();
}
public static SerializableSingleton getInstance() {
return Holder.INSTANCE;
}
protected Object readResolve() {
return getInstance();
}
}
readResolve()方法可以保证反序列化时返回同一个实例。
在分布式环境下,传统的单例模式会遇到挑战:
java复制public class DistributedSingleton {
private static final String LOCK_KEY = "distributed_singleton_lock";
private static final int EXPIRE_TIME = 30000; // 30秒
private DistributedSingleton() {}
public static DistributedSingleton getInstance(Jedis jedis) {
// 尝试获取分布式锁
String result = jedis.set(LOCK_KEY, "locked", "NX", "PX", EXPIRE_TIME);
if ("OK".equals(result)) {
try {
// 检查是否已存在实例
byte[] instance = jedis.get("singleton_instance".getBytes());
if (instance == null) {
DistributedSingleton newInstance = new DistributedSingleton();
jedis.set("singleton_instance".getBytes(),
serialize(newInstance));
return newInstance;
}
return deserialize(instance);
} finally {
// 释放锁
jedis.del(LOCK_KEY);
}
}
// 等待并重试
return getInstance(jedis);
}
}
对于高并发场景,可以考虑以下优化:
java复制public class OptimizedSingleton {
private static volatile OptimizedSingleton instance;
private OptimizedSingleton() {}
public static OptimizedSingleton getInstance() {
OptimizedSingleton temp = instance;
if (temp == null) {
synchronized (OptimizedSingleton.class) {
temp = instance;
if (temp == null) {
temp = new OptimizedSingleton();
instance = temp;
}
}
}
return temp;
}
}
这种实现:
某些场景下,我们需要线程级别的单例:
java复制public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocal =
ThreadLocal.withInitial(ThreadLocalSingleton::new);
private ThreadLocalSingleton() {}
public static ThreadLocalSingleton getInstance() {
return threadLocal.get();
}
}
每个线程获取的都是自己的单例实例,适合线程隔离的场景。
测试单例类需要注意:
java复制public class SingletonTest {
@AfterEach
public void tearDown() throws Exception {
Field instance = SomeSingleton.class.getDeclaredField("instance");
instance.setAccessible(true);
instance.set(null, null);
}
@Test
public void testSingleton() {
SomeSingleton first = SomeSingleton.getInstance();
SomeSingleton second = SomeSingleton.getInstance();
assertSame(first, second);
}
@Test
public void testConcurrentAccess() throws InterruptedException {
final int threadCount = 100;
final CountDownLatch startLatch = new CountDownLatch(1);
final CountDownLatch endLatch = new CountDownLatch(threadCount);
final Set<SomeSingleton> instances = Collections.synchronizedSet(new HashSet<>());
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
startLatch.await();
instances.add(SomeSingleton.getInstance());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endLatch.countDown();
}
}).start();
}
startLatch.countDown();
endLatch.await();
assertEquals(1, instances.size());
}
}
单例对象生命周期长,容易导致内存泄漏。检测方法:
java复制public class LeakFreeSingleton {
private static LeakFreeSingleton instance;
private WeakReference<Context> contextRef;
private LeakFreeSingleton(Context context) {
this.contextRef = new WeakReference<>(context);
}
public static synchronized LeakFreeSingleton getInstance(Context context) {
if (instance == null) {
instance = new LeakFreeSingleton(context.getApplicationContext());
}
return instance;
}
}
使用WeakReference避免持有Activity等短生命周期对象的强引用。
虽然单例模式很实用,但并非所有场景都适用。在某些情况下,可以考虑以下替代方案:
java复制public class App {
private final SomeService service;
@Inject
public App(SomeService service) {
this.service = service;
}
}
优点:
java复制public class ObjectPool<T> {
private final int maxSize;
private final Queue<T> pool;
public ObjectPool(int maxSize, Supplier<T> creator) {
this.maxSize = maxSize;
this.pool = new ArrayBlockingQueue<>(maxSize);
for (int i = 0; i < maxSize; i++) {
pool.offer(creator.get());
}
}
public T borrowObject() {
return pool.poll();
}
public void returnObject(T obj) {
if (pool.size() < maxSize) {
pool.offer(obj);
}
}
}
适合场景:
java复制public final class StringUtils {
private StringUtils() {}
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
}
当只需要一组相关方法而不需要维护状态时,静态工具类比单例更合适。
根据多年项目经验,我总结了以下单例模式的最佳实践:
在最近的一个电商平台项目中,我们使用单例模式管理价格计算规则,通过静态内部类实现,既保证了线程安全,又实现了延迟加载,系统性能提升了15%。关键是要根据具体场景选择合适的实现方式,并充分理解各种实现的优缺点。