目录
[3、双检锁/双重校验锁(DCL,即 double-checked locking)](#3、双检锁/双重校验锁(DCL,即 double-checked locking))
一、定义
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
单例模式涉及到一个单一的类,单例类只能自己创建唯一的一个实例,并提供了一个全局访问点使所有其他对象获取到类的唯一实例。
特点:
- 唯一实例:确保类只有一个实例。
- 全局访问点:提供一个全局访问点来获取类的实例。
- 线程安全:在多线程环境中,确保实例的唯一性。
二、应用场景
1、配置管理:应用程序可能需要一个只初始化一次的配置管理器。
2、日志记录:日志记录器通常只需要一个实例,以避免日志信息的混乱。
3、性能优化:某些对象的创建成本很高,使用单例模式可以避免重复创建。
三、6种实现方式
分类:
- 饿汉式:类加载就会导致该单实例对象被创建。
- 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建。
1、懒汉式,线程不安全。
在这种情况下,我们只有调用 getInstance 方法, instance 对象才会被创建。
在多线程情况下,很多线程都来请求获取该对象,这样就可能创建出多个对象,导致线程不安全。
/**
* 懒汉式 线程不安全
*/
public class SingletonLazy {
//定义一个私有构造方法
private SingletonLazy(){}
//创建该类对象
private static SingletonLazy instance;
//对外提供静态方法获取该对象
public static SingletonLazy getInstance(){
if(instance==null){
instance = new SingletonLazy();
}
return instance;
}
}
2、懒汉式,线程安全
由于上述会出现线程不安全问题,所以我们通过加同步锁,保证每次只有一个线程可以对当前对象进行操作,确保了线程安全。但加锁会影响效率。
/**
* 懒汉式 线程安全
*/
public class SingletonLazy {
//定义一个私有构造方法
private SingletonLazy(){}
//创建该类对象
private static SingletonLazy instance;
//对外提供静态方法获取该对象 , 加锁
public static synchronized SingletonLazy getInstance(){
if(instance==null){
instance = new SingletonLazy();
}
return instance;
}
}
3、双检锁/双重校验锁(DCL,即 double-checked locking)
我们使用volatile关键字, volatile 关键字可以保证可见性和有序性,这个关键字禁止了对当前修饰的变量上下文重排序。保证了方法的可靠性。采用双锁机制,安全且在多线程情况下能保持高性能。
进行二次判断的原因:
假设有两个线程a,b,他们都去请求我们单例模式下类的实例,当第一个判断的时候,两个线程都会进入判断代码块中进行锁的抢占,最终a抢占到了锁,那么b只能在加锁的代码块外部进行等候。这个时候a创建了对象的实例,完成功能后归还了锁,这个时候线程b马上抢占到了锁,然后进入内部代码块。假如没有第二次判断的话,线程a就会再次创建一个新的对象,导致线程不安全。所以,要在这里再加一次判断。
/**
* 懒汉式 双重校验锁
*/
public class SingletonLazy {
//定义一个私有构造方法
private SingletonLazy(){}
//创建该类对象
private volatile static SingletonLazy instance;
//对外提供静态方法获取该对象 , 加双重锁
public static SingletonLazy getInstance(){
if(instance==null){
synchronized (instance.getClass()){
if(instance == null){
instance = new SingletonLazy();
}
}
}
return instance;
}
}
4、静态内部类方式-------只适用于静态域
静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。
/**
* 静态内部类方式
*/
public class SingletonLazy {
//私有构造方法
private SingletonLazy() {}
//定义静态类
private static class SingletonHolder {
//静态方法
private static final SingletonLazy INSTANCE = new SingletonLazy();
}
//对外提供静态方法获取该对象
public static SingletonLazy getInstance() {
return SingletonHolder.INSTANCE;
}
}
5、饿汉式
它基于 classloader 机制避免了多线程的同步问题,instance 在类装载时就实例化。
/**
* 饿汉式
*/
public class SingletonEhan {
//私有构造方法
private SingletonEhan(){}
//创建并实例化该类对象
private static SingletonEhan instance = new SingletonEhan();
//对外提供静态方法获取该对象
public static SingletonEhan getInstance(){
return instance;
}
}
6、枚举
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
四、总结
一般情况下,使用第5种饿汉式。