单例模式

本文详细介绍了Java中单例模式的多种实现方式,包括饿汉式(静态常量、静态代码块)、懒汉式(线程不安全、同步方法、同步代码块和双重检查)以及静态内部类和枚举单例。着重讨论了双重检查锁定机制在实现线程安全和延迟加载中的作用,同时提到了JDK中的`java.lang.Runtime`类作为单例模式的应用示例。
摘要由CSDN通过智能技术生成

单例模式

定义:所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类中只提供一个取得其对象实例的方法(静态方法)

饿汉式

静态常量实现

这里使用了一个小知识,构造器私有化,防止外部new,通过这个技术可以实现在这个类的内部实例化对象;

在内部使用private static关键字对实例进行修饰,在类装载的过程中就对类进行实例化,通过这样的方式实现单例,避免线程同步问题,但无法起到懒加载的功能,可能造成资源浪费。

class Singleton {
	//1. 构造器私有化,  防止外部 new
	private Singleton() {
    
	}

//2.本类内部创建对象实例
    private final static Singleton instance = new Singleton();

//3. 提供一个公有的静态方法,返回实例对象
	public static Singleton getInstance() { return instance;
	}
}

静态代码块

使用静态代码块的方式与使用静态变量的方式区别不大,类的实例化过程仍然在类的装载过程中实现,因此也是线程安全的。

//饿汉式(静态变量) 
class Singleton {
	//1. 构造器私有化,  外部不能 new 
    private Singleton() {

	}

	//2.本类内部创建对象实例
	private	static Singleton instance;

    static { 
        // 在静态代码块中,创建单例对象
    	instance = new Singleton();
    }

    //3. 提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance() { return instance;
    }
}

以上两种方案中,无论是静态变量还是静态代码块都是线程安全的饿汉式,除此之外,还有线程不安全的饿汉式,用文字来描述就是:

我们以静态变量的代码为例子,依旧使用
private static Singleton instance;
对实例进行修饰,但我们不在类初始化过程中对对象进行实例化,将这个过程延迟(也就是懒加载),在业务需要使用该实例的时候再对该实例进行初始化,具体代码如下:

public static Singleton getInstance() {
	if (instance == null) {
    	instance = new Singleton();
    }
    return instance;
}

根据以上代码,我们很容易发现问题,当并发度较高时,假如有两个线程同时判断instance为空,那么两个线程实例化一个类,造成多次实例化,这也就打破了单例模式,我们称这样子的情况为线程不安全的,那么如何实现线程安全的延迟加载呢,这就可以谈到我们的懒汉式单例模式

懒汉式

同步方法

简单来说,对getInstance()的方法加一个Synchronized关键字,给方法上一个方法级的锁,这样是能保证单例的,但这样有很多的缺点,我其实只想保证我在一开始实例化过程中的线程安全,当类实例化后,只有一个实例,是否线程安全已经不重要了,但因为Synchronized关键字的存在,即使实例已经初始化,依旧需要阻塞,这是效率很差的单例模式,我们不考虑。

同步代码块

同步代码块基本与同步方法一致,基本等同于方法级,因此不推荐。

双重检查

这是推荐使用的,双重检查也是我们开发中经常会使用单例模式实现方案,其优点是:懒加载,线程安全,效率较高;

双重检查的代码中,实例对象使用了volatile关键字修饰的原因:

instance = new Singleton()这段代码是分三步执行的:

  1. 为instance分配内存空间
  2. 初始化instance
  3. 将instance指向分配的内存地址

但是由于JVM具有指令重排的特性,执行顺序有可能会变成1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下导致一个线程获得还没有初始化的实例。例如:线程T1执行了1和3,此时T2调用getInstance()发现instance不为空,因此返回实例,但此时实例还没初始化,使用volatile关键字可以禁止JVM的指令重排,保证在多线程环境下也能正常运行。

具体实现如下代码:

class Singleton {
	private static volatile Singleton instance;
    
    // 构造器私有化
    private Singleton() {}
    
    // 提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题,同时保证了效率,推荐使用
    public static Singleton getInstance() {
    	if(instance == null) {
        	synchronized(Singleton.class) {
            	if(instance == null) {
                	instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

静态内部类

枚举

单例模式在JDK的应用

java.lang.Runtime类是经典的单例模式应用者(饿汉式)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值