主要总结《Java并发编程实战》中“第3章:对象共享”的内容。
前言
在没有Java相关并发知识的前提下,第一次看这本书《Java并发编程实战》,其实有些看不太懂,因为里面的很多知识讲的比较抽象,比如可见性、volatile、final等讲的其实都不深入,所以导致自己理解的也很片面。后来就先专门看了“Java内存模型”相关的知识,再对相关知识理解起来,就要深入一些,所以才有了前面写的4篇关于“Java内存模型”相关的文章。
“第二章:基础知识”主要讲解线程安全性、原子性、加锁机制(主要讲解内置锁、synchronized重入)、用锁保护状态,这些知识在我相关系列的前面4篇中,已经讲的比较清楚,就直接跳过。
今天的这篇文章,主要是对《Java并发编程实战》中“第3章:对象共享”的内容进行总结,这章内容看了2遍,因为相关知识的匮乏,有些知识点还是理解的不全,所以也只能基于自己的理解,对所学内容总结一下,要不然很快就忘了,后续也会再重拾相关知识,再二次理解消化。
可见性
指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
在多线程环境下,一个线程对共享变量的操作对其他线程是不可见的。Java提供了volatile来保证可见性,当一个变量被volatile修饰后,表示着线程本地内存无效,当一个线程修改共享变量后他会立即被更新到主内存中,其他线程读取共享变量时,会直接从主内存中读取。当然,synchronize和Lock都可以保证可见性。synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。
评价:该小节内容主要是讲解的volatile的可见性,提到volatile不具备原子性特性,也引出了synchronize,但是对于可见性,final其实可见性是最强的。这部分内容中规中矩,建议大家将volatile、synchronize和final的可见性、原子性对比起来看,最好能理解他们可见性的原理,以及重排序的机制,这样就能对“可见性”这一概念理解的更加深入。
发布和溢出
发布:发布一个对象,是对象能够在当前作用域之外的代码中使用。比如将对象的引用保存到其它代码可以访问的地方,或者在一个非私有方法中返回对象的引用,简单来说,就是外部可以访问到这个对象和里面的成员或者方法,为了保证多线程下没有问题,需要保证对象内部状态的封装性不被破坏。
书中写的有点像八股文,讲的也有点抽象,我理解其实就拿到一个对象时,需要保证对象内部数据已经完全初始化,然后对象内部的成员和方法,要么就完全封装,不能将修改的方式对外暴露,如果不能做到这一点,就需要保证修改和访问是线程安全的。
溢出:当一个不应该发布的对象被发布时,这种情况被称为溢出(Escaspe)。溢出的情况我总结有2种:
- 将成员变量的引用返回,外部就可以修改数据,多线程下可能会存在问题;
- 在构造函数过程中使this引用溢出。
成员变量溢出
class UnsafeStates {
private String[] states = new String[] {"AK","AL" ...
public String[] getStates {return states;}
}
这个是书中的示例,当你发布这个对象时,任何调用者都可以修改数组中的内容,有人可能会说,我在states前面加一个final,这个其实解决不了问题,因为final可以保证states的地址不被改变,但是不能保证内部的数据不被改变。
this引用溢出
书中的注册示例没有看懂(还是自己太弱了。。。),我给一个前面一篇文章的示例,里面可以从原理讲解this引用是如何引出,这个应该比书中给的示例更能深入理解。
public class FinalReferenceEscapeExample {
final int i;
static FinalReferenceEscapeExample obj;
public FinalReferenceEscapeExample () {
i = 1; /
1 条评论
回复