设计模式中的单例模式

什么是单例模式:

使用一个实例处理处理各种操作,而不是对应每一个操作都会创建一个实例。因为都是用一个实例那么这个实例自然被线程共享了,那么就会出现线程安全问题。

单例模式两种方式:

饿汉模式,懒汉模式。

优缺点:

饿汉模式优点是一开始就被初始化了,不会出现线程问题,缺点是在不使用这个实例时也会创建浪费内存资源,同时也无法适用于需要参数生成实例的场景。

懒汉模式使用才会被创建优点是可以传参,使用才会创建不浪费内存资源但是线程不安全。

Java中6种创建单例模式的方式

1、普通的懒汉模式

public class Singleton {
    private static Singleton instance;
    private Singleton (){}

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

并发编程的时候这种会出现线程安全问题

2、加锁的懒汉模式

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

这种做法虽然线程安全了,但是降低了效率,每次这个实例只能被一个线程调用。

3、双重检验锁

public class Singleton {
    private volatile static Singleton instance; //声明成 volatile
    private Singleton (){}

    public static Singleton getSingleton() {
        if (instance == null) {                         
            synchronized (Singleton.class) {
                if (instance == null) {       
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
   
}

为什么叫双重检查锁,在锁前和锁后都进行了判空操作,因为在第一个if可能进来两个线程先后去获得锁,那么就会创建两个实例。实例使用volatile修饰的原因是instance =  new Singleton(); 不是一个原子操作。它可以拆解为三步。

a. memory = allocate() //分配内存

b. ctorInstanc(memory) //初始化对象

c. instance = memory //设置instance指向刚分配的地址

上面的代码在编译运行时,可能会出现重排序从a-b-c排序为a-c-b。在多线程的情况下会出现以下问题。当线程A在执行第5行代码时,B线程进来执行到第2行代码。假设此时A执行的过程中发生了指令重排序,即先执行了a和c,没有执行b。那么由于A线程执行了c导致instance指向了一段地址,所以B线程判断instance不为null,会直接返回一个未初始化的对象。

4、饿汉式 static final field

public class Singleton{
    //类加载时就初始化
    private static final Singleton instance = new Singleton();
    
    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}

缺点是在不使用这个实例时也会创建浪费内存资源,同时也无法适用于需要参数生成实例的场景。

5、静态内部类懒汉模式:

public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE;
    }  
}

这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。

6、枚举

public enum EasySingleton{
    INSTANCE;
}

创建枚举默认就是线程安全的,所以不需要担心double checked locking

引用

如何正确地写出单例模式 | Jark's Blog

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值