多线程案例--单例模式
目录一.什么是单例模式二.饿汉模式三.懒汉模式3.1.单线程版3.2.多线程版一.什么是单例模式单例模式是校招中最常考的设计模式之一什么是设计模式软件开发过程中会遇到很多常见的问题场景。针对这些问题场景大佬们总结了一套固定的套路按照这个套路实现代码不会吃亏。单例模式能保证某个类在程序中只存在唯一一份实例,而不会创建出多个实例。单例模式具体的实现方式有很多.最常见的是饿汉和懒汉两种。二.饿汉模式类加载的同时就创建实例并且将构造方法私有化尽量早创建class Singleton{ private static Singleton instancenew Singleton();//类加载初始化JVM保证线程安全 public static Singleton getInstance(){ return instance; } private Singleton(){ } }静态成员初始化是在类加载的初始化阶段触发的类加载不是程序一启动就触发而是第一次使用该类时才触发return 只是读操作不涉及修改静态变量只会创建并初始化一次静态变量 / 静态代码块的初始化由JVM 保证线程安全JVM 在类初始化时会自动加锁确保多线程下只会执行一次不会产生线程安全问题。三.懒汉模式3.1.单线程版类加载的时候不创建实例.第一次使用的时候才创建实例尽量晚创建class Singleton { private static Singleton instance null; private Singleton() {} public static Singleton getInstance() { if (instance null) { instance new Singleton(); } return instance; } }如果是单线程的话完全是可以实现单例模式的但是多线程就不行了会产生线程安全问题3.2.多线程版这里需要进行加锁操作以防线程调度导致这种现象产生不再是只实例化一个对象class SingletonLazy{ private static SingletonLazy instancenull; private static Object lokernew Object(); public static SingletonLazy getInstance(){ synchronized (loker) { if (instance null) { instance new SingletonLazy(); } System.out.println(Thread.currentThread().getName()解锁); } return instance; } private SingletonLazy(){ } }虽然这个代码已经线程安全了但是还是有问题我们可以看到这个加锁操作并不是按需求加锁如果实例创建了就不涉及到线程安全的问题了按理说就不需要加锁了过度的加锁会导致后续线程阻塞等待降低效率所以我们可以在加锁之前做一个判断class SingletonLazy{ private static SingletonLazy instancenull; private static Object lokernew Object(); public static SingletonLazy getInstance(){ if(instancenull) { System.out.println(Thread.currentThread().getName()判断instance是null的加锁操作避免过度枷锁); synchronized (loker) { System.out.println(Thread.currentThread().getName()被加锁); if (instance null) { System.out.println(Thread.currentThread().getName()判断instance是null的,创建新对象); instance new SingletonLazy(); } System.out.println(Thread.currentThread().getName()解锁); } } return instance; } private SingletonLazy(){ } }第一个if判断是为了避免过度加锁第二个if是为了判断是否已经实例化对象。但是这就完了吗nonono当然没有我们上一张讲到的JVM 为了提高效率会自作主张将长期未变并且用的次数很多的数据存储在寄存器中直接拿取其实JVM 为了优化效率还会进行指令重排的操作。创建实例的步骤1.创建内存。2.构造对象初始化。3.把内存地址赋值给引用。如果不加上限制的话可能会导致指令重排可能顺序为132.就会导致得到的是一个半初始化的对象得到的是一个空格子没有具体内容。class SingletonLazy{ private static volatile SingletonLazy instancenull; private static Object lokernew Object(); public static SingletonLazy getInstance(){ if(instancenull) { System.out.println(Thread.currentThread().getName()判断instance是null的加锁操作避免过度枷锁); synchronized (loker) { System.out.println(Thread.currentThread().getName()被加锁); if (instance null) { System.out.println(Thread.currentThread().getName()判断instance是null的,创建新对象); instance new SingletonLazy(); } System.out.println(Thread.currentThread().getName()解锁); } } return instance; } private SingletonLazy(){ } } public class Demo1{ public static void main(String[] args) { Thread t1new Thread(()-{ SingletonLazy.getInstance(); }); Thread t2new Thread(()-{ SingletonLazy.getInstance(); }); t1.start(); t2.start(); } }