多线程单例模式

1、单例模式

顾名思义,单例模式能保证某个类在程序中只存在唯一一份示例,而不会创建出多个实例。就像java的JDBC编程只需要创建一个单例类DataSourece从这个DataSorce中获取数据库连接。没必要创建多个对象。

单例模式具体实现方式分为“饿汉”和“懒汉”两种。

如何保证一个程序中的单例?

1、人为约定,让大家不去new对象,给大家提供了一个方法,用的时候调用这个方法就行。

2、通过语言自身的语法限制一个类只能存在一个对象。

分析过程:

多线程单例模式_第1张图片

1.1、饿汉模式

代码示例:


/**
 * 单例模式
 */
public class Singleton_Hungry {
    //类的成员变量
    private static Singleton_Hungry instance=new Singleton_Hungry();
    //重写为无参构造方法,修饰符改为私有

    private Singleton_Hungry() {
    }
    //对外提供一个获取成员变量的方法
    public static Singleton_Hungry getInstance(){
        return instance;
    }
}

public class Exe_01 {
    public static void main(String[] args) {
        Singleton_Hungry singleton1=Singleton_Hungry.getInstance();
        Singleton_Hungry singleton2=Singleton_Hungry.getInstance();
        Singleton_Hungry singleton3=Singleton_Hungry.getInstance();
        Singleton_Hungry singleton4=Singleton_Hungry.getInstance();
        //打印类对象
        System.out.println(singleton1);
        System.out.println(singleton2);
        System.out.println(singleton3);
        System.out.println(singleton4);
    }
}

多线程单例模式_第2张图片

1.2、懒汉模式

1.2.1、单线程版

 代码示例:


public class Singleton_Lazy {
    private static Singleton_Lazy instance=null;
    private Singleton_Lazy(){

    }
    public static Singleton_Lazy getInstance(){
        //判断返回的对象是否为空
        if(instance==null){
            //创建对象
            instance=new Singleton_Lazy();
        }
        //返回单例对象
        return instance;
    }
}

public class Exe_03 {
    public static void main(String[] args) {
        //让多线程并发获取单例对象
        for (int i = 0; i < 10; i++) {
            Thread thread=new Thread(() ->{
                //获取单例
                Singleton_Lazy instance=Singleton_Lazy.getInstance();
                System.out.println(instance);
            });
            //启动线程
            thread.start();
        }
    }
}

多线程单例模式_第3张图片

 1.2.2、多线程版

上面的懒汉模式-单线程版的实现是不安全的。

线程安全问题发生在首次创建实例时,如果在多个线程中同时调用getInstance方法,就可能导致创建出多个实例。

一旦实例已经创建好了,后面的多线程环境调用getInstance就不再有线程安全问题了(不再修改instance)。

加上synchronized可以改善这里的线程安全问题

 

public static Singleton_DCL getInstance() {
    synchronized (Singleton_DCL.class) {
        //判断返回的对象是否为空
        //第二次判断,如果为空重新创建对象
        if (instance == null) {
            //创建对象
            instance = new Singleton_DCL();
        }
    }
    //返回单例对象
    return instance;
}

代码示例:

 


public class Singleton_Lazy {
    private static Singleton_Lazy instance=null;
    private Singleton_Lazy(){

    }
    public static Singleton_Lazy getInstance(){
        synchronized(Singleton_Lazy.class) {
            //判断返回的对象是否为空
            if (instance == null) {
                //创建对象
                instance = new Singleton_Lazy();
            }
        }
        //返回单例对象
        return instance;
    }
}

public class Exe_03 {
    public static void main(String[] args) {
        //让多线程并发获取单例对象
        for (int i = 0; i < 10; i++) {
            Thread thread=new Thread(() ->{
                //获取单例
                Singleton_Lazy instance=Singleton_Lazy.getInstance();
                System.out.println(instance);
            });
            //启动线程
            thread.start();
        }
    }
}

 

 多线程单例模式_第4张图片

 1.2.3、双重检查锁(Double Check Locker)DCL

多线程版的单例模式还存在一个严重的问题:

1、当变量没有被初始化的时候,第一次创建可能会出现线程安全问题,因为多个 线程都会创建实例对象。

2、一旦实例对象被创建之后,new操作就永远不会执行了,因为获取到的实例不为null。

3、那么这时就不需要去执行加锁操作,而且加锁也是要去系统申请锁资源,如果拿到锁之后只是判断一下实例对象是否为null,如果为null什么也不干,那么锁资源也就拜拜浪费掉了

所以就出现了双重校验锁,线程安全的单例模式

代码示例:


public class Singleton_DCL {
    private volatile static Singleton_DCL instance=null;
    private Singleton_DCL(){

    }
    public static Singleton_DCL getInstance(){
        //第一次判断是否为空,目的是判断是否需要加锁
        if(instance==null) {
            synchronized (Singleton_DCL.class) {
                //判断返回的对象是否为空
                //第二次判断,如果为空重新创建对象
                if (instance == null) {
                    //创建对象
                    instance = new Singleton_DCL();
                }
            }
        }
        //返回单例对象
        return instance;
    }
}

public class Exe_04 {
    public static void main(String[] args) {
        //让多线程并发获取单例对象
        for (int i = 0; i < 10; i++) {
            Thread thread=new Thread(() ->{
                //获取单例
                Singleton_DCL instance=Singleton_DCL.getInstance();
                System.out.println(instance);
            });
            //启动线程
            thread.start();
        }
    }
}

 多线程单例模式_第5张图片

 

 

你可能感兴趣的:(单例模式,java,开发语言)