单例正确写法
Singleton正确写法:
今天偶然看到设计模式,就复习了一下,这里再对单例进行一个回顾和总结。
1.静态内部类
public class Singleton {
private Singleton(){
}
private static class SingletonFactory{
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonFactory.instance;
}
}
2.枚举
public enum SingtonEnum {
INSTANCE;
//method...
}
3.double-check
public class Singleton {
private static Singleton singleton = null;
private Singleton(){
}
public static Singleton getInstance2(){
if(singleton==null){
{
if (singleton==null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
double-check存在的问题
问题是由指令重排造成的。
对象创建的三个步骤:
1.申请内存
2.初始化
3.引用指向内存地址
指令重排后可能是1,3,2的顺序,两个线程同时访问会出现对象不为空但是没有初始化的问题,所以需要加上volatile,使用内存屏障来禁止指令重排序,来保证执行顺序。volatile保证了屏障前的一定先执行,屏障后的后执行,也就是保证对象一定是这3个操作是被一个线程执行完的,第二个线程拿到的对象是正确的。(但是这三个操作本身还是可以重排序的)
虽然理论上这种单例模式不是线程安全的,但是我测试发现得到的都是相同的示例:
/**
* Created by wxg on 2018/8/14 17:02
*/
public class SingletonTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(10);
CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
try {
for (int i = 0; i < 10; i++) {
service.submit(new Run(cyclicBarrier, "thread-" + i));
}
} finally {
service.shutdown();
}
}
}
class Run implements Runnable {
private CyclicBarrier cyclicBarrier;
private String threadName;
Run(CyclicBarrier cyclicBarrier, String threadName) {
this.cyclicBarrier = cyclicBarrier;
this.threadName = threadName;
}
@Override
public void run() {
try {
cyclicBarrier.await();
System.out.println(threadName + "-> " + Singleton.getInstance2());
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
volatile可见性和禁止重排序
可见性:强制CPU缓存失效,直接读内存。
禁止重排序:使用内存屏障实现,顺序不能跨屏障。
我的个人博客:老吴的博客 和CSDN保持同步,欢迎关注
网络上志同道合,我们一起学习网络安全,一起进步,QQ群:694839022