单例模式:
单例模式:在程序运行期间,确保某些类有且最多只有一个实例对象,并提供一个可访问的全局访问点
单例模式的思路和优势
实现思路
- 静态化实例对象,让实例对象与Class对象互相绑定,通过Class对象就可以直接访问
- 私有化构造对象,禁止通过构造方法创建多个实例(确保单例的核心)
- 提供一个公共的静态方法,用来返回这个类的唯一实例
优势
好处: 尽可能节约内存空间(不为一个类创建多个实例对象),减少GC(垃圾回收)的消耗,并使程序正常运行
饿汉式
指的是JVM在加载类的时候就完成类对象的创建:
代码实现
final class HungrySingleton {
/** 实例对象 */
private static HungrySingleton instance = new HungrySingleton();
/** 私有构造方法 */
private HungrySingleton(){}
/** 获取单例对象,直接返回已创建的实例 */
public static HungrySingleton getInstance(){
return instance;
}
}
优缺点比较
(1)优点:JVM层面的线程安全
JVM在加载这个类的时候就会对它进行初始化,这里包含对静态变量的初始化
(2)缺点:造成空间浪费(空间换时间)
饿汉式是类初始化就创建(不管后续是否使用存在浪费的可能)
类成员变量比较多,可能在没有使用类对象的情况下,一致占用堆内存
懒汉式
指的是在真正需要的时候再完成类对象的创建:
/**
* @author ving
* @Date 2024/10/29
* @Description 懒汉式单例(存在使用线程不安全的问题需要方法加上synchronized关键字实现)
*/
final class LazySingleton {
/** 实例对象 */
private static LazySingleton instance = null;
/** 私有构造方法 */
private LazySingleton(){}
/**
* 线程不安全,可以在方法上使用synchronized关键字实现线程安全
* @return
*/
public static synchronized LazySingleton getInstance(){
if (instance == null){
instance = new LazySingleton();
}
return instance;
}
}
优缺点比较:
(1)优点:节省空间,用到的时候再创建实例对象
需要这个实例的时候,先判断它是否为空,如果为空,再创建单例对象
(2)缺点:影响性能
所有线程访问都会进行同步操作,存在性能问题
双重检查锁模式(DCL)
其实就是线程安全的懒汉式改进:懒汉式方法上加了synchronized每次调用都要加锁,性能不高
/**
* @author ving
* @Date 2024/10/29
* @Description 双重检查锁模式:相当于对线程安全的懒汉式改进:方法上的synchronized每次调用都要加锁影响性能
*/
public class DoubleCheckLockingSingleton {
/**
* 实例对象
*/
private static volatile DoubleCheckLockingSingleton instance = null;
/** 私有构造方法 */
private DoubleCheckLockingSingleton(){}
/**
* 获取单例对象:将方法上的synchonrized移到方法内部
* @return instance 本类的实例
*/
public static DoubleCheckLockingSingleton getInstance(){
//先判断实例是否存在
if (instance ==null){
//加锁创建实例
synchronized (DoubleCheckLockingSingleton.class){
//再次判断实例是否存在
// 此时的其他线程拿到了锁又执行到此处==》这些线程都会创建一个实例,从而创建多个实例(不加volatile存在的指令重排)
if (instance == null){
instance = new DoubleCheckLockingSingleton();
}
}
}
return instance;
}
}
volatile关键字禁止了JVM的指令重排,并且保证线程中对这个变量所做的任何写入操作对其他线程都是即时可见的(保证了内存的可见性)
volatile的作用:
- 可见性:当一个线程修改了 instance 变量的值,新值对其他线程立即可见
- 禁止指令重排:确保 instance 变量的写入操作在读取操作之前完成
静态内部类
静态内部类是线程安全的,也是懒汉式的变形
JVM在加载外部类的过程是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性
/**
* @author ving
* @Date 2024/10/29
* @Description 静态内部类模式,也称作单持有者模式:线程安全,懒汉式的一种实现方式
*/
public class StaticInnerSingleton {
//禁用构造方法
private StaticInnerSingleton(){}
/**
* **延迟加载使用的时候才创建对象,没有加锁的线程安全**
* @return
*/
public static StaticInnerSingleton getInstance(){
return SingletonHolder.instance;
}
//通过静态内部类创建单例对象
private static class SingletonHolder{
private static StaticInnerSingleton instance= new StaticInnerSingleton();
}
}
两者比较
- 饿汉式: 程序启动后一定会加载到的类
- 懒汉式:工具类(项目可能会引用到jar包,但未必会使用到这个工具类,懒汉式可以避免提前被加载到内存中,占用系统资源)