怎么实现一个线程安全的单例模式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了怎么实现一个线程安全的单例模式相关的知识,希望对你有一定的参考价值。

设计模式在面试中挺常考的,单例模式是考的最多的。线程安全的单例还有很多种实现方法。可以使用静态内部类、双重校验锁、静态代码块等。

1、饿汉式
public class Singleton()
    private static Singleton instance = new Singleton();
    private Singleton()
    public static Singleton getInstance()
        return instance;
    

2、懒汉式
public class Singleton()
    private static Singleton instance;
    private Singleton()
    public static synchronized Singleton getInstance()
        if(instance==null)
            instance = new Singleton();
        
        return instance;
    

参考技术A 最简单的办法就是在单例模式所在的类中方法上加同步锁

多线程 实现单例模式 ( 饿汉懒汉 ) 实现线程安全的单例模式 (双重效验锁)

@TOC


单例模式

什么是单例模式

要求我们代码中的某个类,只能有一个实例,不能有多个实例。
实例就是对象。
就是说某个类只能new 一个对象,不能new多个对象。

比如:


单例模式的两种经典实现

单例模式中有两个典型实现:

  1. 饿汉模式
  2. 懒汉模式

我们来通过一个生活上的例子来给大家讲讲什么是饿汉模式,什么是懒汉模式。

但是在计算机中,普遍认为 懒汉模式 比 饿汉模式好。
主要因为 懒汉模式 的效率更高


1. 饿汉模式 (线程安全)

//饿汉模式
class Singleton
    // 1、使用 static 创建一个实例,并且立即进行实例化,
    private  static  Singleton instance = new Singleton();
    // 2、为了防止程序员在其他地方不小心new这个 Singleton,需要把这个类的构造方法设置为 private
    private Singleton();
    //3、提供一个方法,让外面能够拿到唯一的实例。
    public static Singleton getInstance()
        return instance;
    

分析:


2. 懒汉模式 (线程不安全)

由于比较懒,你让我干活,我才开始,否则我不会主动干的~

​ 说人话就是: 类加载时,不急着初始化对象,第一次调用,初始化对象,后边再次调用,直接返回第一次创建好的对象.

//单例模式 - 懒汉模式
class Singleton2
    //1、现在就不是立即初始化实例
    private static Singleton2 instance;// 默认值:Null
    //2、把构造方法设为 private
    private Singleton2();
    //3、提供一个公开的方法,来获取这个 单例模式的唯一实例
    public static Singleton2 getInstance()
        // 只有当我们真正用到这个实例的时候,才会真正去创建这个实例
        if(instance == null)
            instance = new Singleton2();
        
        return instance;
    


public class Test20 
    public static void main(String[] args) 
        Singleton2 instance = Singleton2.getInstance();
    

分析:


区别:


其实在计算机很多其它场景中,也会涉及这情况。

一个典型的案例:


实现线程安全的单例模式

1. 懒汉模式+synchronized静态同步方法 (线程安全但效率差)

说到让一个代码线程安全,我们自然而然的就想到加锁!
但是问题就在于:在哪个地方加锁合适呢?
其实也很好观察,将 if 语句的执行操作 给 加锁,使其两个操作为原子性。
直白来说: 就是 if 语句 打包成“一个整体”,就跟前面分析 count++ 一样。
一致性执行完。

加锁范围 一定要包含 if 语句!!!
要不然没有效果,就像下面这样!

本来我们是想将 读 和 写 操作,打包成一个整体,
但是现在只是 针对写操作进行加锁,这时候就跟没加锁 一样,是没有区别的。

请大家注意!并不是代码中有 synchronized,一定就是线程安全的。
这需要看 synchronized 加的位置,也要正确。
所以 synchronized 写的位置。不能随便。

回过头来,我们再来看一下 synchronized 锁的对象写我们应该些什么。

//单例模式 - 懒汉模式
class Singleton2
    //1、就不是立即初始化实例
    private static volatile Singleton2  instance;// 默认值:Null
    //2、把构造方法设为 private
    private Singleton2();
    //3、提供一个公开的方法,来获取这个 单例模式的唯一实例
    public static Singleton2 getInstance()
            // 只有当我们真正用到这个实例的时候,才会真正去创建这个实例
            synchronized(Singleton2.class)
                if(instance == null)
                    instance = new Singleton2();
                
            
        return instance;
    

虽然我们确实通过上述加锁操作,解决了 if 语句 的原子性问题。


2. 懒汉模式+二次判断(双重校验锁,线程安全且效率高)

用法:

class Singleton 
    private static volatile Singleton instance = null;
    private Singleton() 
    public static Singleton getInstance()  
        if (instance == null)   //第5行
            synchronized (Singleton.class)  //第6行
                if (instance == null)   //第7行
                    instance = new Singleton(); //第8行
                 
        
        return instance; //第9行
    

分析:

即:


以上是关于怎么实现一个线程安全的单例模式的主要内容,如果未能解决你的问题,请参考以下文章

多线程阻塞队列定时器线程安全的单例模式的原理及实现

多线程阻塞队列定时器线程安全的单例模式的原理及实现

多线程 实现单例模式 ( 饿汉懒汉 ) 实现线程安全的单例模式 (双重效验锁)

python实现线程安全的单例模式

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

实现线程安全的单例模式