博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
对象发布
阅读量:6903 次
发布时间:2019-06-27

本文共 3645 字,大约阅读时间需要 12 分钟。

0.摘要

  主要讨论了在多线程并发环境下,安全发布的几种方式(多种单例模式演示 )。

1.基本概念

  发布对象:使对象能够被当前范围之外的代码所看见。比如通过类的非私有方法返回对象的引用。

  对象逸出:一种错误的发布。当一个对象还没有构造完成时,就被其他线程所看见

  不正确的发布对象会导致两种错误:

    1)发布线程以外的任何线程都可以看到被发布对象的过期的值 

    2)线程看到的被发布线程的引用是最新的,然而被发布对象的状态却是过期的

  如何安全发布对象?四种方法

    1)在静态初始化函数中初始化一个对象引用

    2)将对象的引用保存到volatile类型域或者AtomicReference对象中

    3)将对象的引用保存到一个由锁保护的域中

    4)将对象的引用保存到某个正确构造对象的final类型域中(本文目前没涉及到)

2.懒汉模式

  (1)最简单的一段:在第一次使用的时候创建

1 public class SingletonExample1 { 2     private SingletonExample1() { 3     } 4     private static SingletonExample1 instance = null; 5  6     public static SingletonExample1 getInstance() { 7         if (instance == null) { 8             instance = new SingletonExample1(); 9         }10         return instance;11     }12 }

   分析:单线程的情况下,线程是安全的。在多线程的情况下,第7-9行代码会出现线程不安全的问题。两个线程同时调用这段代码的时候会拿到两个不同的实例,尽管并不会产生什么不好的情况,但是如果这个私有构造方法在实现的时候要做很多操作,如资源的释放、运算等。那么这样产生的单例是线程不安全的。


 

  (2)推荐和线程安全一段:上述可以单独使用synchronized修饰getInstance()来保证线程安全,但是有可能会造成性能损耗。更好的方法

1 /** 2  * 懒汉模式-双重同步锁单例模式 3  * 单例在使用时候创建 4  */ 5 public class SingletonExample1 { 6     private SingletonExample1() { 7     } 8  9     //单例对象10     private static SingletonExample1 instance = null;11 12     //静态工厂方法13     public static SingletonExample1 getInstance() {14         if (instance == null) {
//双重检测机制15 synchronized (SingletonExample1.class) {
//同步锁16 if (instance == null) {17 instance = new SingletonExample1();18 }19 }20 }21 return instance;22 }23 }

  分析:但上述的代码并不是线程安全的,插播一个小知识点。

这里有一个知识点:CPU指令相关 在上述代码中,执行new操作的时候,CPU一共进行了三次指令 (1)memory = allocate() 分配对象的内存空间 (2)ctorInstance() 初始化对象 (3)instance = memory 设置instance指向刚分配的内存

  在程序运行过程中,CPU为提高运算速度和JVM优化会做出违背代码原有顺序的优化。我们称之为乱序执行优化或者说是指令重排。 

  那么上面知识点中的三步指令极有可能被优化为(1)(3)(2)的顺序。当我们有两个线程A与B,A线程遵从132的顺序,经过了两此instance的空值判断后,执行了new操作,并且cpu在某一瞬间刚结束指令(3),并且还没有执行指令(2)。而在此时线程B恰巧在进行第一次的instance空值判断,由于线程A执行完(3)指令,为instance分配了内存,线程B判断instance不为空,直接执行return,返回了instance,这样调用这个实例的时候会出现错误。

  如何解决指令重排?

在对象声明时使用volatile(双重检测)关键字修饰,阻止CPU的指令重排。private volatile static SingletonExample instance = null;

3.饿汉模式

  (1)最简单的一段:在类装载的时候创建,能保证线程安全

1 //饿汉模式,在类装载的时候创建 2 public class SingletonExample1 { 3     private SingletonExample1() { 4     } 5      6     private static SingletonExample1 instance = new SingletonExample1(); 7  8     public static SingletonExample1 getInstance() { 9         return instance;10     }11 }

  分析:第一如果其私有构造函数中存在大量处理,那么类装载的时间会很长,第二如果只进行了类的加载却没有调用,会造成性能的浪费。(下面的分析有点长)


  (2)通过静态块来初始化,注意静态代码块一定要在静态工厂方法的上面

1 public class SingletonExample { 2     // 私有构造函数 3     private SingletonExample() { 4     } 5     // 单例对象 6     private static SingletonExample instance = null; 7     static { 8         instance = new SingletonExample(); 9     }10     // 静态的工厂方法11     public static SingletonExample getInstance() {12         return instance;13     }14 }

4.枚举模式

1 /** 2  *枚举模式 3  */ 4 public class SingletonExample2 { 5     private SingletonExample2() { 6     } 7  8     public static SingletonExample2 getInstance() { 9         return Singleton.INSTANCE.getSingleton();10     }11 12     private enum Singleton {13         INSTANCE;14         private SingletonExample2 singleton;15 16         //JVM来保证这个方法只被调用一次17         Singleton() {18             singleton = new SingletonExample2();19         }20 21         public SingletonExample2 getSingleton() {22             return singleton;23         }24     }25 26 }

  分析:由于枚举类的特殊性,JVM可以保证枚举类的构造函数只被调用一次,相比饿汉模式,在实际调用的时候才做初始化。相比懒汉模式更加安全。推荐使用。

5.小结

  本文章是作者学习并发编程中的笔记总结,方面我温故,希望能帮到大家。主要总结了几种创建单例的方法,推荐使用枚举模式。

2019-05-14 16:09:33

转载于:https://www.cnblogs.com/truekai/p/10861102.html

你可能感兴趣的文章
Js点餐加减数量
查看>>
【转】ACM训练计划
查看>>
Design Tic-Tac-Toe
查看>>
LeetCode 477: Total Hamming Distance
查看>>
win10安装MarkdownPad 2报错This view has crashed的处理及md简单语法
查看>>
Unity3D - 设计模式 - 工厂模式
查看>>
第二十六课:jQuery对事件对象的修复
查看>>
Leetcode题目:Swap Nodes in Pairs
查看>>
Windows聚焦转为图片
查看>>
POJ NOI0101-09 字符菱形
查看>>
jQuery--停止动画和判断是否处于动画状态stop()
查看>>
1-1 接口自动化测试框架从设计到开发
查看>>
MYSQL常用命令
查看>>
js 打开新页面 window.open()
查看>>
Intellij idea 一个窗口打开多模块并添加依赖
查看>>
生成自己的js工具包,包括打包webpack、测试mocha、生成文档jsdoc、发布npm包的操作...
查看>>
企业构建大数据分析体系的4个层级
查看>>
[Linux学习]man page常用按键
查看>>
Django中的CSRF
查看>>
iPhone 物理尺寸与分辨率
查看>>