zl程序教程

您现在的位置是:首页 >  其他

当前栏目

设计模式之单例设计模式

2023-03-07 09:45:33 时间
  • 单例的实现

- 懒汉式
    - 线程不安全
    - 线程安全
- 饿汉式
- DCL(双重校验锁)
- 登记式/静态内部类

单例设计模式:

1.保证一个类只有一个实例 (控制某些共享资源的访问权限 如 数据库或文件)

2.为该实例提供一个全局访问节点

实现步骤:

1.将默认构造方法设置为私有,防止其他对象使用单例类 new运算符

2.新建一个静态构建方法作为构造函数,该函数自动调用私有构造函数来创建对象,并将其保存在一个静态成员变量中。此后 所有对于该函数的调用都将返回这一缓存对象

懒汉式(线程不安全)

public class Singleton {

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

}

懒汉式(线程安全) 说真正用到它的时候才去创建实例 synchronized锁的粒度太大

public class Singleton {

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

三、饿汉式(程序刚启动时就创建了实例)

public class Singleton {

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

}

四、DCL(双重校验锁)

public class Singleton {

private static Singleton singleton = null;

private Singleton(){}

public static Singleton getInstance(){

/*

一堆业务处理代码

*/

if(null == singleton){

synchronized(Singleton.class){//锁粒度变小

if(null == singleton){//DCL

try {

Thread.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

singleton = new Singleton();

}

}

}

return singleton;

}

public static void main(String[] args) {

for (int i=0;i<1000;i++){

new Thread(()-> System.out.println(Singleton.getInstance().hashCode())).start();

}

}

}

通过在第10行又加了一层if判断,也就是所谓的Double Check Lock。也就是说即便拿到锁了,也得去作一步判断,如果这时判断对像不为空,那么就不用再创建对象,直接返回就可以了,很好的解决了“改进2”中的问题。但这里第8行是不是可以去了,我个人觉得都行,保留第8行的话,是为了提升效率,因为如果去了,每个线程过来就直接抢锁,抢锁本身就会影响效率,而if判断就几ns,且大部分线程是不需要抢锁的,所以最好保留。 到这DCL 单例的原理就介绍完了,但是还是有一个问题。就是需要考虑指令重排序的问题,因此得加入volatile来禁止指令重排序。继续分析代码,为了分析方便这里将Singleton代码简化

---

单例模式使用DCL(双重检验锁的)的原理:

单例模式 有饿汉式和懒汉式,

饿汉式在程序刚启动就创建实例,虽然线程安全,但是比较浪费资源, 懒汉式在 在需要的时候才会去创建实例,但是在多线程并发的情况下存在线程不安全的问题,

优化1:是加synchronize关键字,但是synchronize关键字直接加在放上下粒度太大。

优化2:使用 synchronize(Singleton.class){try{}catch()} 来减少锁的粒度,但是并发就会又存在问题 当线程1在准备抢锁的时候可能会被阻塞(如时间片到),这时候线程二 上cpu运行,顺利拿到锁并结束运行释放锁。线程1 恢复,拿到锁吗,运行结束。造成了的线程不安全.

优化3: 尽管某一个线程抢到了锁,也要判断当前 是否已存在实例化的对象,存在了就不在创建,不存在在去实例化。 优化4:就是需要考虑指令重排序的问题,因此得加入volatile来禁止指令重排序