什么是单例模式
单例模式(Singleton Pattern)是一种设计模式,用于确保一个类在整个应用程序运行期间只有一个实例,并且提供一个全局访问点来访问该实例。这种模式确保一个类只有一个实例存在,并提供一个访问它的全局变量。
举例:任务管理器(只能打开一个窗口)
单例模式的作用
- 控制实例数量:
-
- 确保某个类只有一个实例存在。对于一些管理类、资源类等,这种特性尤为重要。
- 全局访问点:
-
- 提供一个全局访问点,可以通过静态方法或变量来访问单例实例,简化了对象访问和管理。
- 节省资源:
-
- 由于只创建一个实例,可以避免创建多个实例带来的资源浪费,尤其是那些需要频繁访问但创建代价较高的类。
- 状态共享:
-
- 由于单例对象在应用程序中只有一个实例,可以通过它来共享状态和数据,避免了多实例带来的状态不一致问题。
饿汉
在类加载的同时创建实例
class Singleton {
// 静态变量,类加载时创建实例
private static final Singleton instance = new Singleton();
// 私有构造函数,防止外部实例化
private Singleton() {}
// 提供全局访问点
public static Singleton getInstance() {
return instance;
}
}
懒汉—单线程
在被使用的时候创建实例
class Singleton {
private static Singleton instance = null;
// 私有构造函数,防止外部实例化
private Singleton() {}
// 提供全局访问点
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉—多线程
上面的写法在多线程中其实会造成线程安全问题。如果两个线程同时判断instance == null,就会创建两次Singleton。
所以我们最简单的方法就是给方法加锁。
class Singleton {
private static Singleton instance = null;
// 私有构造函数,防止外部实例化
private Singleton() {}
// 提供全局访问点
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
那么如果很多线程都来调用这个方法,竞争锁的频率就太高了。我们可以使用双重if来解决这个问题,在竞争前先判断一下。
class Singleton {
private static volatile Singleton instance = null;
// 私有构造函数,防止外部实例化
private Singleton() {}
// 提供全局访问点
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
如何理解呢?为什么要加volatile?
我们直接看运行步骤吧
- 假设有三个线程,同时执行getInstance,通过最外层的if判断知道实例还没有开始创建,于是开始竞争锁
- 假设线程1竞争到了锁,于是判断第二个if是否为true,如果为true开始实例化。因为加上了volatile所以变量在被修改时会及时更新到内存。
- 当线程1释放锁之后,线程2、3依次拿到锁,但此时第二层if判断为false。
- 其他线程后面再调用方法时第一层if就进不去,所以直接减少了锁的竞争。