单例模式:只允许创建一个实例对象
饿汉式 还没使用就已经在类装载过程中完成实例化了
单例模式用private static final 来修饰类的实例化方法
并用private 来修饰构造方法
给外界暴露用static修饰的getInstance 方法来获取实例对象。
懒汉式 : 要用了才创建
在getInstance方法里 先判断Instance是否为空 再创建 但是要注意多线程时的安全问题 可以用synchronized 修饰getInstance方法 但是锁的范围太大效率不高 有些代码不用锁 所以要细化锁
尝试在判断是否为空后再加锁,但是不可行因为在一个线程判断为空后,还没往下执行,另一个线程也判断为空 然后往下执行 new一个对象 执行完毕后释放锁 第一个线程依然可以获得锁 然后又new了一个对象 所以不行
所以要使用Double Check Lock (DCL)双重锁判定 在获得锁后再判断一次Instance是否为空 然后再new对象
但是还有一个问题 Instance 对象 需不需要volatile修饰
Volatile有两个作用:1.线程间可见 2.禁止指令重排序
线程间可见:
不加volatile 修饰running时 t.running = false 对于线程t1不可见 m不会打印m end!
Volatile的变量的含义就是 当一个线程改变了这个值后 volatile通知其他线程不能在线程的本地缓存中读这个值 要到内存重新读
禁止指令重排序:
具体例子 就是 线程一: a=1; x =b; 线程二: b=1; y=a;
一.Start();二.Start()
要是指令重排序就会出现 x0&& y0 的情况
现在回到
首先线程1来了 new了对象半初始化 m的默认值是0 还未赋值
New有三条指令第一条是申请内存空间 半初始化 m的默认值是0 第二条是使用构造方法
第三条是建立关联
这时如果发生了指令重排序 那么就会先建立关联指向了半初始化对象 再调用构造方法给对象赋值
这时要是第二个线程在调用构造方法赋值之前进入 就会使用已经半初始化的对象 但是它的值是0 而不是真正的值 所以会出问题 所以要用volatile来禁止指令重排序
之前用final修饰Instance现在不用 而是用volatile 是因为 final修饰的变量 不可变 volatile可以