饿汉模式:
本身线程安全,在类加载时就已经进行了实例化,无论之后用不用的到。
package com.cljtest.demo.thread;
public class HungerSingleton {
    public static HungerSingleton hungerSingleton = new HungerSingleton();
    public static HungerSingleton getInstance(){
        return hungerSingleton;
    }
    //构造方法私有化
    private HungerSingleton(){
    }
    public static void main(String[] args) {
        //起10个线程进行结果测试
        for(int i = 0;i<10;i++){
            new Thread(()->{
                System.out.println(HungerSingleton.getInstance());
            }).start();
        }
    }
}一定要注意无参构造方法的私有化问题,如果
设成了public,其他类里实例化时直接new一个无参的构造方法,会造成单例模式失去意义。
由于类加载时就已经实例化了,所以下面的的一切都是基于类加载时创建的对象进行的操作,所以线程安全,都是同一个实例化对象。
运行结果如下:
可以看到地址都是同一个,没有改变。
懒汉模式:
是在需要的时候才实例化,最简单的写法是线程不安全的。
package com.cljtest.demo.thread;
public class LazySingleton {
    public static LazySingleton lazySingleton = null;
    private LazySingleton(){
    }
    public static LazySingleton getInstance() throws InterruptedException {
        if(null == lazySingleton){
            //模拟操作中的停顿
            Thread.sleep(1000L);
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
    public static void main(String[] args) {
        for(int i = 0;i<10;i++){
            new Thread(()->{
                try {
                    System.out.println(LazySingleton.getInstance());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
可以看到结果,地址几乎均不相同,每次的实例化都是一个新的对象,线程是不安全的。
如果想要将懒汉模式变成线程安全的,需要对实例化的操作进行加锁处理。需要将getInstance()方法进行如下修改。
public static LazySingleton getInstance() throws InterruptedException {
    if(null == lazySingleton){
        //模拟操作中的停顿
        Thread.sleep(1000L);
        //为了线程安全,需要加锁
        synchronized (LazySingleton.class) {
            //双重判定,对象没实例化时再创建
            if(null == lazySingleton) {
                lazySingleton = new LazySingleton();
            }
        }
    }
    return lazySingleton;
}这时候运行会发现基本没有问题了,输出的地址都是一个。
但是对这个类进行反编译可能会进行指令重排序的操作,也会造成线程的不安全,所以需要将变量加上volatile关键字进行修饰,他有禁止指令重排序的作用。
public static volatile LazySingleton lazySingleton = null;来源:CSDN
作者:jia718
链接:https://blog.csdn.net/jia718/article/details/103655773