单例模式
单例模式:一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式实现
普通饿汉模式
/**
* 普通饿汉模式
*/
public class Singleton {
/**
* 类加载时进行实例化对象
*/
private static final Singleton SINGLETON = new Singleton();
/**
* 私有构造,防止外部new对象
*/
private Singleton() {
}
/**
* 通过静态方法获取对象实例
*
* @return
*/
public static Singleton getInstance() {
return SINGLETON;
}
public void test() {
System.out.println("普通饿汉模式");
}
}
调用方式
Singleton singleton = Singleton.getInstance();
singleton.test();
优缺点:
优点: 类加载时就进行实例化,之后的操作效率会很高。
缺点: 由于类加载时就进行实例化,如果后续不对此类进行任何操作,就会导致内存的浪费。
线程不安全的懒汉模式
/**
* 懒汉式(线程不安全)
*/
public class SingletonTwo {
private static SingletonTwo instance;
/**
* 私有构造,防止外部new对象
*/
private SingletonTwo() {
}
/**
* 通过静态方法获取对象实例
*
* @return
*/
public static SingletonTwo getInstance() {
if(instance == null) {
instance = new SingletonTwo();
}
return instance;
}
public void test() {
System.out.println("懒汉式(线程不安全)");
}
}
调用方式
SingletonTwo singleton = SingletonTwo.getInstance();
singleton.test();
优缺点:
优点: 在第一次调用的时候才进行实例化。
缺点: 当多个线程同时进入到 if(instance == null) {...} 时,会创建多个对象。
同步锁懒汉式
/**
* 同步锁懒汉式(线程安全,效率低)
*/
public class SingletonThree {
private static SingletonThree instance;
/**
* 私有构造,防止外部new对象
*/
private SingletonThree() {
}
/**
* 通过静态方法获取对象实例
*
* @return
*/
public static synchronized SingletonThree getInstance() {
if(instance == null) {
instance = new SingletonThree();
}
return instance;
}
public void test() {
System.out.println("同步锁懒汉式(线程安全,效率低)");
}
}
调用方式:
SingletonThree singleton = SingletonThree.getInstance();
singleton.test();
优缺点:
优点: 在第一次调用的时候才进行实例化,且线程安全。
缺点: 使用 synchronized 的方式对方法加锁,会影响效率。
双重校验锁懒汉式
/**
* 双重校验锁懒汉式(线程安全,且多线程环境下可以保持高性能)
*/
public class SingletonFour {
/**
* volatile是为了防止指令重排序
*/
private static volatile SingletonFour instance;
/**
* 私有构造,防止外部new对象
*/
private SingletonFour() {
}
/**
* 通过静态方法获取对象实例
*
* @return
*/
public static SingletonFour getInstance() {
if(instance == null) {
synchronized (SingletonFour.class) {
if(instance == null) {
instance = new SingletonFour();
}
}
}
return instance;
}
public void test() {
System.out.println("双重校验锁懒汉式(线程安全,且多线程环境下可以保持高性能)");
}
}
调用方式:
SingletonFour singleton = SingletonFour.getInstance();
singleton.test();
优缺点
优点: 在第一次调用的时候才进行实例化,且线程安全,效率较高。
缺点: 实现复杂,且 volatile 需要在JDK1.5之后的版本才能确保安全。
当没有volatile关键字的时候是线程不安全的。
public class SingletonExample4 {
private SingletonExample4(){}
private static SingletonExample4 instance = null;
//线程不安全
//当执行instance = new SingletonExample4();这行代码时,CPU会执行如下指令:
//1.memory = allocate() 分配对象的内存空间
//2.ctorInstance() 初始化对象
//3.instance = memory 设置instance指向刚分配的内存
//单纯执行以上三步没啥问题,但是在多线程情况下,可能会发生指令重排序。
// 指令重排序对单线程没有影响,单线程下CPU可以按照顺序执行以上三个步骤,但是在多线程下,如果发生了指令重排序,则会打乱上面的三个步骤。
//如果发生了JVM和CPU优化,发生重排序时,可能会按照下面的顺序执行:
//1.memory = allocate() 分配对象的内存空间
//3.instance = memory 设置instance指向刚分配的内存
//2.ctorInstance() 初始化对象
//假设目前有两个线程A和B同时执行getInstance()方法,A线程执行到instance = new SingletonExample4(); B线程刚执行到第一个 if (instance == null){处,
//如果按照1.3.2的顺序,假设线程A执行到3.instance = memory 设置instance指向刚分配的内存,此时,线程B判断instance已经有值,就会直接return instance;
//而实际上,线程A还未执行2.ctorInstance() 初始化对象,也就是说线程B拿到的instance对象还未进行初始化,这个未初始化的instance对象一旦被线程B使用,就会出现问题。
public static SingletonExample4 getInstance(){
if (instance == null){
synchronized (SingletonExample4.class){
if(instance == null){
instance = new SingletonExample4();
}
}
}
return instance;
}
}
静态内部类懒汉式
/**
* 静态内部类懒汉式
*/
public class SingletonFive {
/**
* 私有构造,防止外部new对象
*/
private SingletonFive() {
}
/**
* 通过静态方法获取对象实例
*
* @return
*/
public static SingletonFive getInstance() {
return Singleton.SINGLETON;
}
public void test() {
System.out.println("静态内部类懒汉式");
}
/**
* 静态内部类实例化对象
*/
private static class Singleton {
/**
* 类加载时进行实例化对象
*/
private static final SingletonFive SINGLETON = new SingletonFive();
}
}
调用方式:
SingletonFive singleton = SingletonFive.getInstance();
singleton.test();
优缺点
优点: 只有在调用 getInstance() 方法的时候,静态内部类才会被加载,从而对主类(我们需要的类)进行实例化,即线程安全,又效率高。
缺点: 多创建一个类。
枚举类饿汉式(防止反序列化)
/**
* 枚举类饿汉式(防止反序列化)
*/
public class SingletonSix {
private SingletonSix(){}
public static SingletonSix getInstance(){
return Singleton.INSTANCE.getInstance();
}
private enum Singleton{
INSTANCE;
private SingletonSix singleton;
//JVM保证这个方法绝对只调用一次
Singleton(){
singleton = new SingletonSix();
}
public SingletonSix getInstance(){
return singleton;
}
}
public void test() {
System.out.println("枚举类饿汉式(防止反序列化)");
}
}
调用方式:
SingletonSix singleton = SingletonSix.getInstance();
singleton.test();
优缺点
优点: 实现简单,防止反序列化生成多个实例,且线程安全。
缺点: Enum 需在JDK1.5之后版本使用。
单例模式的优缺点
- 优点
单例模式在内存中只有一个实例,减少了内存开支,尤其是频繁的创建和销毁实例。
由于只生成一个实例,所以减少了系统的性能开销。
避免对资源的多重占用,例如写文件操作。
单例模式可以在系统设置全局的访问点,优化和共享资源访问。 - 缺点
单例模式不易扩展,若要扩展,除了修改代码外别无他法。
单例模式对测试不利。
单例模式与单一职责原则有冲突,一个类应该只实现一个逻辑,而不用关心它是否是单例的。
偶做前堂客
祝你天天开心
在未知的时间
在未知的地点
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了