序文:
如果你没有时间阅读这本,那你就来对地方了。小编会将每一章刨去废话,取其精华。分享给大家。如果觉得可以请不要忘了关注小编。我会定期跟新java 中的经典书籍。
:
第三章:对象的共享(如何共享和发布对象)
1.当读操作和写操作不在一个线程中时,就要考虑可见性问题了。
在没有同步的情况下,编译器,处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整。
下面赋值操作可能比上面的先被赋值
2.一个线程想要读取一个共享状态的不是失效值,那么就得进入到这个对象的保护锁中获取。
失效值:即多线程中获取到的不是该共享状态的最新值
3.加锁机制既可以确保可见性有可以确保原子性,volatile 只能确保可见性
4..volatile:轻量级的同步机制
作用:将修改后的值立刻写入(可以理解为一个变量的修改和写入是同步的,读取不是)
使用场景:常用于标记状态
读取volatile变量的开销只比非volatile变量略高一些
不能保证变量自增的原子性(不能保证原子性)
5.何时使用volatile:
对变量的写入操作不依赖变量的当前值(只管写入,不用先检测后执行),或者只有单个线程更新变量的值
该变量不会与其他变量一起纳入不变性条件中
在访问变量时不需要加锁
6.发布一个对象:使对象能够在当前作用域之外的代码中使用(例子:将一个指向该对象的引用保存到其他代码中可以访问的地方)
发布内部的状态会破坏封装性(本来是私有的但是现在作为参数传给了别的对象的方法)
简单的理解就是让别的代码可以访问到
7.逸出:当某个不应该发布的对象被发布(不该被访问的对象,而不被获取到了-返回值---)
常见的场景:当发布内部对象的时候(内部对象相当于持有了外部对象的this,可以访问所有的外部属性,所以很有可能逸出)
不要在构造函数中使用this逸出。
不要在构造函数中启动线程。
8.线程封闭:将某个对象封闭在一个线程中,那么就是线程安全的(单线程中对变量的操作都是安全的)
应用:数据库链接:一个线程一个链接
9.单线程系统的优点:
避免死锁
大多数GUI框架都是单线程的
10.栈封闭:(其实就是方法中定义对象使用对象)
只能通过局部来访问对象。(无状态)
局部变量的固有属性之一就是封闭在执行线程中。
11.ThreadLocal: (可以理解为只有当前线程才能访问的,一个map)
12.不可变对象一定是线程安全的。
对象创建以后其状态就不能修改
对象的所有域都是final(或者私有不提供修改)
没有this引用逸出
13.final:域能确保初始化过程的安全性,从而可以不受限制的访问不可变对象,并共享这些对象时无需同步。
良好封装和将不可变域声明为final,是良好的编程习惯
final 如果指向一个可变对象引用,(对象中有可变状态),那么这个在使用这个变量的时候任然需要同步。
14.安全发布一个正确构造的对象。(例如ConcurrentMap 中添加的对象)
在静态初始化函数中初始化一个对象引用
将对象的引用保存到volatile 类型的域或者AtomicReferance对象中
将对象的引用保存到某个正确构造对象的final类型域中
将对象的引用保存到一个由锁保护的域中(单例中长会用锁来保护创建对象)
补充:
静态初始化器是由jvm 内部的同步机制来确保被正确的发布的
总结:安全发布--就是多次调用公布出这个对象的方法,都不会出现失效值的情况。
例子:在全局域中 public Animal animal=new Animal(); 这样发布一个对象,是不安全的。
即使没有修改操作,也可能在多线程的环境下获取这个属性的时候获取到没有初始化好的对象。
如果是public static ....那么是安全的发布,但是如果在多线程下修改是不能保证可见性,会获取到失效值
15.如果对象发布以后不会被修改,那么对于其他在没有额外同步情况下安全地访问这些对象线程来说,安全发布就够了。
16.事实不可变对象:就是被创建出来以后对象是不安全的但是,事实业务逻辑上不会修改
17.对于可变对象,不经要安全的发布而且要同步,才能保证线程安全。
18总结:
在单线程情况下,啥都别考虑,直接码代码。
在多线程情况下,没有共享变量的,都在方法中创建对象,属性。。。啥都别考虑。
如果创建这个对象不可以变,只需要考虑安全创建就可以。
如果多线程,有共享,这个共享只有一个线程执行修改写入,考虑使用volatile
如果多线程,且有共享状态,且可变:考虑同步
领取专属 10元无门槛券
私享最新 技术干货