啥叫设计模式?
设计模式就好比我们下象棋中的棋谱,红方当头炮,黑方马来跳。针对红发的一些走法,黑方下的时候有一些固定套路,按照套路来走局势就会吃亏。
就好比你打游戏玩某一个英雄,按照一定的打法打就不会打的太烂。
而单例设计模是设计模式中非常常见的一种设计模式,单例模式能保证某个类在程序中只存在唯一一份实例,而不会创建多个实例。就比如我们在JDBC编程中,数据库的一些URL用户名就是唯一的实例。
单例设计模式非为两种,一种饿汉式和懒汉式
饿汉模式其实叫做立即加载,立即加载有着 着急、急迫的意思,所以又叫饿汉模式。饿汉模式是指使用类的时候已经将对象创建完毕了。
饿汉模式代码:
把构造方法设置成私有的,防止调用者在类外修改类的实例,把实例设置成也设置为私有同时设为类属性。
我们这里用静态内部类实现
public class TestDemo { //懒汉式 static class Singleton{ private static Singleton instance = new Singleton(); //把构造方法设置私有,防止类外再创建实例 private Singleton() { } //此处只涉及到读取操作,不存在线程安全问题 public Singleton getInstance() { return instance; } } }
延时加载又叫懒汉模式,它是在调用 get() 方法实例才被创建。
来看一下代码:
这个代码在当单线程下肯定是没有问题的,但是在多线程下是肯定是会发生线程安全题的。
public class TestDemo2 { //懒汉式 static class Singleton { private static Singleton instance = null; private Singleton() { } //这里涉及到多线程,多个线程同时涉及到修改的时候,会出现现线程不安全的问题 public static Singleton getInstance() { if (instance == null) { //如果实例还没有创建,涉及到多个线程时,会涉及到修改,会发生线程安全问题 instance = new Singleton(); } //如果实例已经创建就不存在安全问题 return instance; } } }
就比如 if 判断这里的操作,假设我们有两个线程同时执行 if 操作。现是线程1执行了 if 操作为 null,接着线程2也执行了 if 操作 也为 null。
两个线程都进去了这个 if ,先是 线程1 执行了 new 操作,接着就是 线程 2执行了 new 操作,把之前的实例给覆盖了。好像覆盖了并没有什么影响。
举个列子:这个对象在极端情况下, new 这个对象需要消耗 10G内存,要好几分钟的时间。那能这么搞吗?
解决办法就是把 if 和 new 对象这两个对象变成原子的,也就是使用 synchronized 加锁。保证了原子性。
getInstance() 方法是在首次调用的时候才会涉及到安全问题,所以我们在这加了一把锁。
那如果后续调用也不是会涉及到加锁操作吗?
后续本来没有线程安全问题了,已经不需要加锁,再尝试加锁,那不是多此一举吗?
加锁本来就是一个开销比较大的操作,此处不应该加锁的地方加锁了,可能会让这个代码的速度降低很多倍。
改进思路:首次调用的时候加锁,后续调用就不加锁了。
在最外层再加上一个 if 条件,区分是首批调用还是后续调用。
第一层 if :区分是首批线程还是后续调用的线程,决定是不是要加锁
因为 synchronized 加锁那里会发生阻塞等待,而且还不指定等多久,通过最外层的 if 之间限制了
里面这一层 if 是看第一批首先抢到锁的幸运儿才能访问的
当首批线程通过第一层if,进入到锁阶段,并创建好对象之后,这个时候,相当于已经把内存中 insertance的值修改成非 null了
后续批次的线程,通过第一层 if 的时候,也需要判定 instance 的值,但是这个判定不一定是从内存读取数据,也可能是从寄存器读数据了
你以为这样就完了吗?其实并没有。
由于编译器的优化,在第一层 if 的时候,线程可能会之间去访问CPU寄存器里的值,这时候CPU寄存器里存的可能还是 null ,那么第一次 if 可能就失效了。
这就属于内存可见性的问题,
解决办法很简单 之间在volatile修饰 instance保证内存可见性。
线程安全的懒汉式代码:
public class TestDemo3 { //线程安全的懒汉式 static class Singleton { //加 volatile 保证内存可见性,防止编译器的优化。导致第一层 if 失效 private volatile static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { //这一层防止太多线程抢锁 if(instance == null) { synchronized (Singleton.class) { //只有一个幸运儿才能到这里 if (instance == null) { instance = new Singleton(); } } } return instance; } } }