Java四种实现单例模式
饿汉式
/**
* 1.饿汉式:线程安全,耗费资源
* 场景:
* 资源共享:当需要在多个模块中共享同一个实例时
* 全局访问点:作为全局唯一的访问点,例如日志记录器、配置管理器等。
* 线程安全要求高:饿汉式单例模式在类加载时就创建实例,因此不存在线程安全问题,适合多线程环境下使用。
* 避免频繁创建销毁:如果创建和销毁实例的代价比较大,可以使用饿汉式单例模式来避免频繁的创建和销毁操作。
*
*
*/
class EagerSingleton1 {
//该对象的引用不可修改
private static final EagerSingleton1 ourInstance = new EagerSingleton1();
public static EagerSingleton1 getInstance() {
return ourInstance;
}
private EagerSingleton1() {}
}
懒汉式(线程不安全)
class LazySingleton1 {
private static LazySingleton1 ourInstance;
private LazySingleton1() {}
public static LazySingleton1 getInstance() {
if (null == ourInstance) {
ourInstance = new LazySingleton1();
}
return ourInstance;
}
}
懒汉式(加锁)
/**
* 3.(线程安全)懒汉式:给方法加锁
* 过重,因为访问时也要加锁,后面双检查就是为了减轻
*/
class LazySingleton2 {
private static LazySingleton2 ourInstance;
public synchronized static LazySingleton2 getInstance() {
if (null == ourInstance) {
ourInstance = new LazySingleton2();
}
return ourInstance;
}
private LazySingleton2() {}
}
4.懒汉式(双重检查)
/**
* 4.线程安全的懒汉式:双重检查锁(同步代码块)
* 为了解决上述查询也要加锁过重的情形
*/
class Singleton3 {
private static Singleton3 ourInstance;
public static Singleton3 getInstance() {
if (null == ourInstance) {
synchronized (Singleton3.class) {
if (null == ourInstance) {
ourInstance = new Singleton3();
}
}
}
return ourInstance;
}
private Singleton3() {}
}
懒汉式(推荐,静态内部类特性)
/**
* (双私有一公开)
* 6.线程安全的懒汉式:静态内部类(推荐)
* 静态内部类在被加载时不会立即初始化,只有在第一次使用时才会加载并初始化。
*/
class LazySingleton4 {
private LazySingleton4() {
}
private static class SingletonHolder {
private static LazySingleton4 ourInstance = new LazySingleton4();
}
public static LazySingleton4 getInstance() {
return SingletonHolder.ourInstance;
}
}
对比
- 饿汉式单例模式:
优点:线程安全,在类加载时就创建实例,简单易用。
缺点:可能导致资源浪费,不支持延迟加载。
推荐情况:适用于资源消耗较小且需要在整个应用程序中共享的单例对象。
- 懒汉式单例模式:
优点:支持延迟加载,避免了资源浪费。
缺点:在多线程环境下需要考虑线程安全性,可能需要额外的同步控制。
推荐情况:适用于需要延迟加载、资源消耗较大或不一定会被频繁使用的单例对象。
- 双重检查锁(Double-Checked Locking):
优点:在需要时才进行同步控制,提高了性能,支持延迟加载。
缺点:实现复杂,需要考虑内存可见性和指令重排序问题。
推荐情况:适用于需要延迟加载、线程安全且性能要求较高的单例对象。
- 静态内部类:
优点:实现简单,线程安全且支持延迟加载。
缺点:在某些场景下可能存在类加载顺序问题(极少发生)。
推荐情况:适用于需要延迟加载、线程安全且简单易用的单例对象。
总体来说,如果需要延迟加载、线程安全且性能要求较高,推荐使用双重检查锁