论坛首页 Java企业应用论坛

浅谈Java中的同步的方法和原理

浏览 8271 次
精华帖 (1) :: 良好帖 (4) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-07-09   最后修改:2010-07-13
Java的内存模型中Thread会附有自己的堆栈,寄存器,必要时需要和主存即heap之间同步。
可以使用Synchornized关键字和Concurrent包中的Lock可以保证线程互斥和可见性。

互斥性体现在类锁或者对象锁上,每个对象自身都包含一个监视器,该监视器是一个每次只能被一个线程所获取进入的临界区,可以通过wait和notify来退出和准入临界区。可以看出这是一个生产者-消费者的模型。而Concurrent包中的Lock为了能够获得更好的性能和更好的扩展性,以及不依赖于关键字的可读代码,自己实现了这样一个生产消费队列,也就是AbstractQueuedSynchronizer,被称为AQS的机制。每个Lock都内置了一个AbstractQueuedSynchronizer。需要说明的是AbstractQueuedSynchronizer内部实现采用了CAS机制,通过getState, setState, compareAndSetState访问控制一个32bit int的形式进行互斥。

那么可见性是如何保证的呢?

对于关键字的同步机制,其实可见性就是线程和主存之间的同步时机问题。共有4个时间点需要注意:
1 获取或释放类锁/对象锁的时候。Thread保证reload/flush全部变更
2 volatile就是flush on write或者reload on read
3 当线程首次访问共享变量时,可以得到最新的结果。
题外:所以在构造方法中公布this时很危险的。简单的说,就是构造时不逃脱任何变量,不开启新的线程,只做封装。关于安全构造,请参考
http://www.ibm.com/developerworks/cn/java/j-jtp0618/#resources
4 线程结束时,所有变更会写回主存

关于Concurrent Lock如何实现可见性的问题,Doug Lea大侠,只在他的论文中提到,按照JSR133,Unsafe在getState, setState, compareAndSetState时保证了线程的变量的可见性,不需要额外的volatile支持,至于具体这些native做了哪些magic就不得而知了,总之,最后的contract就是保证lock区间的共享变量可见性。开发团队被逼急了就这样回答:
引用
There seems to be a real reluctance to explain the dirty details. I think the question was definitely understood on the concurrent interest thread, and the answer is that synchronized and concurrent locking are intended to be interchangable in terms of memory semantics when implemented correctly. The answer to matfud's question seems to be "trust us."

不过这个地方的确是开发团队给我们用户迷惑的地方,在同样应用了CAS机制的Atomic类中,都内嵌了volatile变量,但是再lock块中,他告诉我们可以保证可见性。

感兴趣的同学可以下面的两个thread和Doug Lea的thesis:
http://altair.cs.oswego.edu/pipermail/concurrency-interest/2005-June/001587.html
http://forums.sun.com/thread.jspa?threadID=631014&start=15&tstart=0
http://gee.cs.oswego.edu/dl/papers/aqs.pdf
   发表时间:2010-07-11  
楼主能详细或举例解释一下“不逃脱任何变量”中逃脱吗?是英文escape吗?
0 请登录后投票
   发表时间:2010-07-11  
mark~~
字数补丁
0 请登录后投票
   发表时间:2010-07-12  
读不懂,lz写的不够系统不够详细,对于我这种初学者太费力了!
0 请登录后投票
   发表时间:2010-07-13  
既然是浅谈,为何如此抽象。。。。。
0 请登录后投票
   发表时间:2010-07-18  
CAS和LL/SC操作都属于Nonblocking Synchronization,能够保证是Happens-Before的偏序,但是只能保证有限的正确性,比如SUM求和等。但是Lock能够保证读写的顺序。

比如,顺序定义如下:T1(W) -> T2(R) -> T3(W),如果HB的关系,那么T2会看到T3写入以后的结果。而Lock,就能够保证顺序。

0 请登录后投票
   发表时间:2010-07-19  
找到答案了,这个技术叫做Piggybacking,我还不知道怎么翻译。总之就是利用既有的happens-before来优化,而不是通过同步来产生新的happens-before。
0 请登录后投票
   发表时间:2010-07-20  
yangyi 写道
找到答案了,这个技术叫做Piggybacking,我还不知道怎么翻译。总之就是利用既有的happens-before来优化,而不是通过同步来产生新的happens-before。


Atomic类利用的是CAS操作,这个需要硬件(指令)支持。
0 请登录后投票
   发表时间:2010-07-20  
mercyblitz 写道
yangyi 写道
找到答案了,这个技术叫做Piggybacking,我还不知道怎么翻译。总之就是利用既有的happens-before来优化,而不是通过同步来产生新的happens-before。


Atomic类利用的是CAS操作,这个需要硬件(指令)支持。


AQS也是CAS的吧,而且CAS对于操作的原子变量内存模型语义和synchronization是等同的,CAS如果不考虑Piggybacking并不能保证全部共享变量的可见性。这里说的是Concurrent Lock可见性的问题。
0 请登录后投票
   发表时间:2010-07-20  
yangyi 写道
mercyblitz 写道
yangyi 写道
找到答案了,这个技术叫做Piggybacking,我还不知道怎么翻译。总之就是利用既有的happens-before来优化,而不是通过同步来产生新的happens-before。


Atomic类利用的是CAS操作,这个需要硬件(指令)支持。


AQS也是CAS的吧,而且CAS对于操作的原子变量内存模型语义和synchronization是等同的,CAS如果不考虑Piggybacking并不能保证全部共享变量的可见性。这里说的是Concurrent Lock可见性的问题。


AQS利用了CAS操作,最开始它的state信息利用了内存屏蔽原语volatile。我的理解是,AQS利用偏序(Happens-Before)是没有问题的,因为在执行上这些线程并没有区别,因为只要确保只有一个线程在工作,其他线程被park掉。那么AQS不需要同步synchronized原语,照样可以工作良好。因此Happens-Before的可见性,不会影响线程的工作。相反,Non-blocking 同步的性能更加。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics