java实现线程安全的单例模型

2023年6月15日08:09:17

单例模式:确保某要给类只有一个实例,并提供一个全局访问带你来访问这个实例。

单例模式分为饿汉模型和懒汉模式

//饿汉模式:(线程安全)
public class Singleton1 {
    // 静态私有成员变量
    private static Singleton1 instance = new Singleton1();
    // 私有构造函数
    private Singleton1() {
    }
  // 静态公有工厂方法,返回唯一实例
    public static Singleton1 getInstance() {
        return instance;
    }
}
// 懒汉模式:(线程不安全,需要通过双重检查锁定机制控制)(双检锁)
public class Singleton2 {
   // 静态私有成员变量
    private static Singleton2 instance = null;
  // 私有构造函数
    private Singleton2() {
    }
  // 静态公有工厂方法,判断成员变量是否为空,不为空则实例化
    public static Singleton2 getInstance() {
        if(instance == null)
            instance = new Singleton2();
        return instance;
    }
}

优缺点:

饿汉模型不需要考虑线程安全问题,调用速度和访问速度优于懒汉模型,但是由于它不管是是否被调用都会提前创建类的实例,所以资源利用率比较低,系统加载时间比较长。
懒汉模式实现了延迟加载,但是需要克服多线程同时访问的问题,需要通过双检锁(双重检查锁定机制)来进行控制,导致性能受到一定影响。

为什么懒汉模型线程不安全?

假设有两个线程AB,其中A执行到检查方法,判断实例为空null,那么A会得到ture的结果,但是B线程运行,也有可能出现判断实例为空null的情况,那么两个线程都会执行instance = new Singleton();从而创建了两个实例。

双检锁(双重检查锁定机制)

为了避免以上这种尴尬的情况,需要将这两行代码加上同步锁。但这还不够完美,每次调用函数得到实例都要试图加上一个同步锁(类锁),而加锁是一个非常耗时的操作,没有必要的情况下应该尽量避免。基于这种想法,我们可以在加锁前再次判断实例是否为空。这就是双重检查锁定机制。

还有一点,定义 instance 变量时需要使用 volatile 进行修饰,因为需要保证 instance 变量发生修改后可以及时将结果刷新到主内存中,对其他线程可见。

public class Singleton3 {
  // 私有静态成员变量
    private static volatile Singleton3 instance = null;
  // 私有构造函数
    private Singleton3() {
    }
  // 共有静态工厂方法
    public static Singleton3 getInstance() {
  // 判断 instance 是否为空,为空->加锁,创建实例(为了进程安全,再次判断),不为空->返回实例
        if(instance == null) {
            synchronized (Singleton3.class) {
                if(instance == null)
                    instance = new Singleton3();
            }
        }
        return instance;
    }
}

使用静态内部类创建实例(推荐使用)

public class Singleton{
	//私有构造函数
	private Singleton(){
	}
    //静态内部类
    private static class HolderClass{
    	private static final Singleton instance = new Singleton();
    }
	//静态公有工厂方法,返回内部类中创建的实现
	public static singleton getInstance(){
		return HolderClass.instance;
	}
}

加载Singleton类的过程中,会为类变量在方法区中分配内存空间并初始化,但是,并不会加载静态内部类HolderClass.
当调用Singleton.getInstance(),执行return HolderClass.instance语句时,HolderClass类才会被加载,instance对象才会被初始化。

上面的解释说明使用静态内部类创建实例是 懒加载 的; HolderClass 只会加载一次,保证了 instance 是单例的;类加载过程是线程安全的,保证 instance 初始化的过程是线程安全的。

通过枚举实现单例模型

利用枚举的特性,让JVM来帮我们保证线程安全和单一实例

public enum Singleton {

    INSTANCE;

    public void doSomething() {
        System.out.println("doSomething");
    }
}

public class Main {

    public static void main(String[] args) {
        Singleton.INSTANCE.doSomething();
    }

}


  • 作者:梵法利亚
  • 原文链接:https://blog.csdn.net/wg22222222/article/details/123331295
    更新时间:2023年6月15日08:09:17 ,共 2031 字。