1、单例模式
顾名思义,单例模式能保证某个类在程序中只存在唯一一份示例,而不会创建出多个实例。就像java的JDBC编程只需要创建一个单例类DataSourece从这个DataSorce中获取数据库连接。没必要创建多个对象。
单例模式具体实现方式分为“饿汉”和“懒汉”两种。
如何保证一个程序中的单例?
1、人为约定,让大家不去new对象,给大家提供了一个方法,用的时候调用这个方法就行。
2、通过语言自身的语法限制一个类只能存在一个对象。
分析过程:

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);
    }
}
 

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();
        }
    }
}
 

1.2.2、多线程版
上面的懒汉模式-单线程版的实现是不安全的。
线程安全问题发生在首次创建实例时,如果在多个线程中同时调用getInstance方法,就可能导致创建出多个实例。
一旦实例已经创建好了,后面的多线程环境调用getInstance就不再有线程安全问题了(不再修改instance)。
加上synchronized可以改善这里的线程安全问题
代码示例:
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();
        }
    }
}
 
 
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();
        }
    }
} 
 










