「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」。
序言
单例模式在实际工作中的应用场景非常多,概念非常简单,就是整个程序中,有且只允许有一个实例。例如:我们程序中存放一个当前用户信息的类,我们使用单例模式,可以确保在任何页面获取信息时拿的都是同一个对象,这样可以保持状态的一致性。
单例模式的类型
也许会有疑惑,单例模式为啥还会有类型,这是因为实例的创造时机导致。
- 懒汉式:只有在需要使用时,才去创建实例
- 饿汉式:在类加载时就先创建好,等待被使用
懒汉式创建单例对象
懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。否则则先执行实例化操作。
public class Singleton {
private static Singleton singleton;
private Singleton(){}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
以上方式其实还会存在一个隐形问题,就是如果两个线程同时判断singleton为null,那么他们都会去实例化一个对象,这样就成了多例。所以,我们要解决的是线程安全问题。
我们常用的方法,就是加个锁
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
// 或者
public static Singleton getInstance() {
synchronized(Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
return singleton;
}
这样就规避了两个线程同时创建Singleton对象的风险。
但是引来另外一个问题:每次去获取对象都需要先获取锁,并发性能非常地差,极端情况下,可能会出现卡顿现象。 所以我们提出优化设想:目标是如果没有实例化对象则加锁创建,如果已经实例化了,则不需要加锁,直接获取实例
public static Singleton getInstance() {
if (singleton == null) { // 线程A和线程B同时看到singleton = null,如果不为null,则直接返回singleton
synchronized(Singleton.class) { // 线程A或线程B获得该锁进行初始化
if (singleton == null) { // 其中一个线程进入该分支,另外一个线程则不会进入该分支
singleton = new Singleton();
}
}
}
return singleton;
}
饿汉式创建单例对象
饿汉式在加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就要马上创建这个对象,不需要等到被调用时再去创建。
public class Singleton{
private static final Singleton singleton = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return singleton;
}
}
注意上面的代码在第3行已经实例化好了一个Singleton对象在内存中,不会有多个Singleton对象实例存在
类在加载时会在堆内存中创建一个Singleton对象,当类被卸载时,Singleton对象也随之消亡了。