﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-卓凡-文章分类-core java</title><link>http://www.blogjava.net/zhuofan/category/43483.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 11 Jan 2010 20:24:52 GMT</lastBuildDate><pubDate>Mon, 11 Jan 2010 20:24:52 GMT</pubDate><ttl>60</ttl><item><title>（转）Java轻量级锁原理详解(Lightweight Locking)</title><link>http://www.blogjava.net/zhuofan/articles/308755.html</link><dc:creator>卓凡</dc:creator><author>卓凡</author><pubDate>Fri, 08 Jan 2010 09:59:00 GMT</pubDate><guid>http://www.blogjava.net/zhuofan/articles/308755.html</guid><wfw:comment>http://www.blogjava.net/zhuofan/comments/308755.html</wfw:comment><comments>http://www.blogjava.net/zhuofan/articles/308755.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuofan/comments/commentRss/308755.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuofan/services/trackbacks/308755.html</trackback:ping><description><![CDATA[<p>大家知道，Java的多线程安全是基于Lock机制实现的，而Lock的性能往往不如人意。<br />
原因是，monitorenter与monitorexit这两个控制多线程同步的bytecode原语，是JVM依赖操作系统互斥(mutex)来实现的。<br />
互斥是一种会导致线程挂起，并在较短的时间内又需要重新调度回原线程的，较为消耗资源的操作。</p>
<p>为了优化Java的Lock机制，从Java6开始引入了轻量级锁的概念。</p>
<p>轻量级锁（Lightweight Locking）本意是为了减少多线程进入互斥的几率，并不是要替代互斥。<br />
它利用了CPU原语Compare-And-Swap(CAS，汇编指令CMPXCHG)，尝试在进入互斥前，进行补救。</p>
<p>本文将详细介绍JVM如何利用CAS，实现轻量级锁。</p>
<p>&nbsp;</p>
<h2>原理详解</h2>
<p>Java Object Model中定义，Object Header是一个2字（1 word = 4 byte）长度的存储区域。<br />
第一个字长度的区域用来标记同步，GC以及hash code等，官方称之为 <strong>mark word</strong>。第二个字长度的区域是指向到对象的Class。</p>
<p>在2个word中，mark word是轻量级锁实现的关键。它的结构见下表</p>
<p><img class="alignnone" src="http://photo2.bababian.com/usr832855/upload1/20091108/sEr6VcNWQ3MGSJfkYoZB6knsSUGuVEm2zpVYUxJM37QJs8MmlBYJH4A==.jpg" alt="" width="654" height="181" /></p>
<p>从表中可以看到，state为lightweight locked的那行即为轻量级锁标记。bitfieds名为指向lock record的指针，这里的<strong>lock record，其实是一块分配在线程堆栈上的空间区域</strong>。<br />
用于CAS前，拷贝object上的mark word(为什么要拷贝，请看下文)。<br />
第三项是重量级锁标记。后面的状态单词很有趣，inflated，译为膨胀，在这里意思其实是锁已升级到OS-level。<br />
在本文的范围内，我们只关注第二和第三项即可。</p>
<p>为了能直观的理解lock，unlock与mark word之间的联系，我画了一张流程图：</p>
<p><img class="alignnone" src="http://photo2.bababian.com/usr832855/upload1/20091108/sTTfLdDTZyHJ6QelroYs4ee17rTplaZNFotdaxHwNxmr4W76SoEGUtg==.jpg" alt="" width="682" height="692" /></p>
<p>在图中，提到了拷贝object mark word，由于脱离了原始mark word，官方将它冠以displaced前缀，即<strong>displaced mark word</strong>(置换标记字)。<br />
这个displaced mark word是整个轻量级锁实现的关键，在CAS中的compare就需要用它作为条件。</p>
<p><strong>为什么要拷贝mark word？<br />
</strong>其实很简单，原因是为了不想在lock与unlock这种底层操作上再加同步。</p>
<p>在拷贝完object mark word之后，JVM做了一步<strong>交换指针的操作</strong>，即流程中第一个橙色矩形框内容所述。<br />
将object mark word里的轻量级锁指针指向lock record所在的stack指针，作用是让其他线程知道，该object monitor已被占用。<br />
lock record里的owner指针指向object mark word的作用是为了在接下里的运行过程中，识别哪个对象被锁住了。</p>
<p>下图直观地描述了交换指针的操作。</p>
<p><img class="alignnone" src="http://photo2.bababian.com/usr832855/upload1/20091108/suJ+8n3D3ayQqYuOXJd6PFn3jdrlAsNHPVN6SNRgge_MoEa3AW7FVmA==.jpg" alt="exchange_pointer_1" width="685" height="333" /></p>
<p><img class="alignnone" src="http://photo2.bababian.com/usr832855/upload1/20091108/s+qV8_wc99ti7gP+AhG27qHjoQjGDqqeeBRwrIZs8PgWDV+Bk2Bne+A==.jpg" alt="" width="670" height="457" /></p>
<p>最后一步unlock中，我们发现，JVM同样使用了CAS来验证object mark word在持有锁到释放锁之间，有无被其他线程访问。<br />
如果其他线程在持有锁这段时间里，尝试获取过锁，则可能自身被挂起，而mark word的重量级锁指针也会被相应修改。<br />
此时，unlock后就需要唤醒被挂起的线程。</p>
<p>原文链接：<a href="http://kenwublog.com/theory-of-lightweight-locking-upon-cas" rel="bookmark">http://kenwublog.com/theory-of-lightweight-locking-upon-cas</a></p>
<img src ="http://www.blogjava.net/zhuofan/aggbug/308755.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuofan/" target="_blank">卓凡</a> 2010-01-08 17:59 <a href="http://www.blogjava.net/zhuofan/articles/308755.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）java内存模型详解</title><link>http://www.blogjava.net/zhuofan/articles/308754.html</link><dc:creator>卓凡</dc:creator><author>卓凡</author><pubDate>Fri, 08 Jan 2010 09:58:00 GMT</pubDate><guid>http://www.blogjava.net/zhuofan/articles/308754.html</guid><wfw:comment>http://www.blogjava.net/zhuofan/comments/308754.html</wfw:comment><comments>http://www.blogjava.net/zhuofan/articles/308754.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuofan/comments/commentRss/308754.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuofan/services/trackbacks/308754.html</trackback:ping><description><![CDATA[<p><span style="font-size: 15px; font-weight: bold;"> 内存模型 (memory model) </span><br />
内存模型描述的是程序中各变量（实例域、静态域和数组元素）之间的关系，以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节.</p>
<p>不同平台间的处理器架构将直接影响内存模型的结构.</p>
<p>在C或C++中, 可以利用不同操作平台下的内存模型来编写并发程序. 但是, 这带给开发人员的是, 更高的学习成本.<br />
相比之下, java利用了自身虚拟机的优势, 使内存模型不束缚于具体的处理器架构, 真正实现了跨平台.<br />
(针对hotspot jvm, jrockit等不同的jvm, 内存模型也会不相同)</p>
<p><strong>内存模型的特征: </strong><br />
a, Visibility 可视性 (多核,多线程间数据的共享)<br />
b, Ordering 有序性 (对内存进行的操作应该是有序的)</p>
<p><span style="font-size: 15px; font-weight: bold;"> java 内存模型 ( java memory model )</span><br />
根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory)，Java中所有变量都储存在主存中，对于所有线程都是共享的。</p>
<p>每条线程都有自己的工作内存(Working Memory)，工作内存中保存的是主存中某些变量的拷贝，线程对所有变量的操作都是在工作内存中进行，线程之间无法相互直接访问，变量传递均需要通过主存完成。<br />
<img src="http://photo2.bababian.com/usr832855/upload1/20090614/sBtYKP1GU1okcV3oHvBCcWyO9WOwCsqaK9YtCbHK70RnhSeacQjf2Mg==.jpg" alt="java内存模型" /><br />
</p>
<p>其中, 工作内存里的变量, 在多核处理器下, 将大部分储存于处理器高速缓存中, 高速缓存在不经过内存时, 也是不可见的.</p>
<p><strong>jmm怎么体现 可视性(Visibility) ? </strong><br />
在jmm中, 通过并发线程修改变量值, 必须将线程变量同步回主存后, 其他线程才能访问到.</p>
<p><strong>jmm怎么体现 有序性(Ordering) ? </strong><br />
通过java提供的同步机制或volatile关键字, 来保证内存的访问顺序.</p>
<p><span style="font-size: 15px; font-weight: bold;"> 缓存一致性（cache coherency） </span></p>
<p><strong>什么是缓存一致性? </strong><br />
它是一种管理多处理器系统的高速缓存区结构，其可以保证数据在高速缓存区到内存的传输中不会丢失或重复。(来自wikipedia)</p>
<p>举例理解:<br />
假如有一个处理器有一个更新了的变量值位于其缓存中，但还没有被写入主内存，这样别的处理器就可能会看不到这个更新的值.</p>
<p><strong>解决缓存一致性的方法? </strong><br />
a, 顺序一致性模型:<br />
要求某处理器对所改变的变量值立即进行传播, 并确保该值被所有处理器接受后, 才能继续执行其他指令.</p>
<p>b, 释放一致性模型: (类似jmm cache coherency)<br />
允许处理器将改变的变量值延迟到释放锁时才进行传播.</p>
<p>jmm缓存一致性模型 &#8211; &#8220;happens-before ordering(先行发生排序)&#8221;</p>
<p>一般情况下的示例程序:</p>
<p>x = 0;<br />
y = 0;<br />
i = 0;<br />
j = 0;</p>
<p>// thread A<br />
y = 1;<br />
x = 1;</p>
<p>// thread B<br />
i = x;<br />
j = y;</p>
<p>在如上程序中, 如果线程A,B在无保障情况下运行, 那么i,j各会是什么值呢?</p>
<p>答案是, 不确定. (00,01,10,11都有可能出现)<br />
这里没有使用java同步机制, 所以 jmm 有序性和可视性 都无法得到保障.</p>
<p><strong>happens-before ordering( 先行发生排序) </strong>如何避免这种情况?<br />
排序原则已经做到:<br />
a, 在程序顺序中, 线程中的每一个操作, 发生在当前操作后面将要出现的每一个操作之前.<br />
b, 对象监视器的解锁发生在等待获取对象锁的线程之前.<br />
c, 对volitile关键字修饰的变量写入操作, 发生在对该变量的读取之前.<br />
d, 对一个线程的 Thread.start() 调用 发生在启动的线程中的所有操作之前.<br />
e, 线程中的所有操作 发生在从这个线程的 Thread.join()成功返回的所有其他线程之前.</p>
<p>为了实现 happends-before ordering原则, java及jdk提供的工具:<br />
a, synchronized关键字<br />
b, volatile关键字<br />
c, final变量<br />
d, java.util.concurrent.locks包(since jdk 1.5)<br />
e, java.util.concurrent.atmoic包(since jdk 1.5)<br />
&#8230;</p>
<p>使用了happens-before ordering的例子:<br />
<img src="http://photo2.bababian.com/usr832855/upload1/20090614/s4diKhwEj+b2YIu8xsaBw0wD0cj67U4TCOHfIO6R3P5FtzfaY6wrQwg==.jpg" alt="happens-before ordering" /></p>
<p>(1) 获取对象监视器的锁(lock)</p>
<p>(2) 清空工作内存数据, 从主存复制变量到当前工作内存, 即同步数据 (read and load)</p>
<p>(3) 执行代码，改变共享变量值 (use and assign)</p>
<p>(4) 将工作内存数据刷回主存 (store and write)</p>
<p>(5) 释放对象监视器的锁 (unlock)</p>
<p>注意: 其中4,5两步是同时进行的.</p>
<p>这边最核心的就是第二步, 他同步了主内存,即前一个线程对变量改动的结果,可以被当前线程获知!(利用了happens-before ordering原则)</p>
<p><strong>对比之前的例子</strong><br />
如果多个线程同时执行一段未经锁保护的代码段，很有可能某条线程已经改动了变量的值，但是其他线程却无法看到这个改动，依然在旧的变量值上进行运算，最终导致不可预料的运算结果。</p>
<p><span style="font-size: 15px; font-weight: bold;"> 经典j2ee设计模式Double-Checked Locking失效问题 </span><br />
双重检查锁定失效问题,一直是JMM无法避免的缺陷之一.了解DCL失效问题, 可以帮助我们深入JMM运行原理.</p>
<p>要展示DCL失效问题, 首先要理解一个重要概念- 延迟加载(lazy loading).</p>
<p>非单例的单线程延迟加载示例:</p>
<div class="wp_syntax">
<div class="code">
<pre class="java" style="font-family: monospace;"><span style="color: #000000; font-weight: bold;">class</span> Foo<br />
<br />
<span style="color: #009900;">{</span><br />
<br />
<span style="color: #000000; font-weight: bold;">private</span> Resource res <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">public</span> Resource getResource<span style="color: #009900;">(</span><span style="color: #009900;">)</span><br />
<br />
<span style="color: #009900;">{</span><br />
<br />
<span style="color: #666666; font-style: italic;">// 普通的延迟加载</span><br />
<br />
<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">(</span>res <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">)</span><br />
<br />
res <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Resource<span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">return</span> res<span style="color: #339933;">;</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
<span style="color: #009900;">}</span></pre>
</div>
</div>
<p>非单例的 多线程延迟加载示例:</p>
<div class="wp_syntax">
<div class="code">
<pre class="java" style="font-family: monospace;"><span style="color: #000000; font-weight: bold;">Class</span> Foo<br />
<br />
<span style="color: #009900;">{</span><br />
<br />
<span style="color: #000000; font-weight: bold;">Private</span> Resource res <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">Public</span> <span style="color: #000000; font-weight: bold;">synchronized</span> Resource getResource<span style="color: #009900;">(</span><span style="color: #009900;">)</span><br />
<br />
<span style="color: #009900;">{</span><br />
<br />
<span style="color: #666666; font-style: italic;">// 获取实例操作使用同步方式, 性能不高</span><br />
<br />
<span style="color: #000000; font-weight: bold;">If</span> <span style="color: #009900;">(</span>res <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">)</span> res <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Resource<span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">return</span> res<span style="color: #339933;">;</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
<span style="color: #009900;">}</span></pre>
</div>
</div>
<p>非单例的 DCL多线程延迟加载示例:</p>
<div class="wp_syntax">
<div class="code">
<pre class="java" style="font-family: monospace;"><span style="color: #000000; font-weight: bold;">Class</span> Foo<br />
<br />
<span style="color: #009900;">{</span><br />
<br />
<span style="color: #000000; font-weight: bold;">Private</span> Resource res <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">Public</span> Resource getResource<span style="color: #009900;">(</span><span style="color: #009900;">)</span><br />
<br />
<span style="color: #009900;">{</span><br />
<br />
<span style="color: #000000; font-weight: bold;">If</span> <span style="color: #009900;">(</span>res <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">)</span><br />
<br />
<span style="color: #009900;">{</span><br />
<br />
<span style="color: #666666; font-style: italic;">//只有在第一次初始化时,才使用同步方式.</span><br />
<br />
<span style="color: #000000; font-weight: bold;">synchronized</span><span style="color: #009900;">(</span><span style="color: #000000; font-weight: bold;">this</span><span style="color: #009900;">)</span><br />
<br />
<span style="color: #009900;">{</span><br />
<br />
<span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">(</span>res <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">)</span><br />
<br />
<span style="color: #009900;">{</span><br />
<br />
res <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Resource<span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
<span style="color: #000000; font-weight: bold;">return</span> res<span style="color: #339933;">;</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
<span style="color: #009900;">}</span></pre>
</div>
</div>
<p>Double-Checked Locking看起来是非常完美的。但是很遗憾，根据Java的语言规范，上面的代码是不可靠的。</p>
<p>出现上述问题, 最重要的2个原因如下:<br />
1, 编译器优化了程序指令, 以加快cpu处理速度.<br />
2, 多核cpu动态调整指令顺序, 以加快并行运算能力.</p>
<p>问题出现的顺序:<br />
1, 线程A, 发现对象未实例化, 准备开始实例化<br />
2, 由于编译器优化了程序指令, 允许对象在构造函数未调用完前, 将 共享变量的引用指向 部分构造的对象, 虽然对象未完全实例化, 但已经不为null了.<br />
3, 线程B, 发现部分构造的对象已不是null, 则直接返回了该对象.</p>
<p>不过, 一些著名的开源框架, 包括jive,lenya等也都在使用DCL模式, 且未见一些极端异常.<br />
说明, DCL失效问题的出现率还是比较低的.<br />
接下来就是性能与稳定之间的选择了?</p>
<p><strong>DCL的替代 Initialize-On-Demand :</strong></p>
<div class="wp_syntax">
<div class="code">
<pre class="java" style="font-family: monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> Foo <span style="color: #009900;">{</span><br />
<br />
<span style="color: #666666; font-style: italic;">// 似有静态内部类, 只有当有引用时, 该类才会被装载</span><br />
<br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">class</span> LazyFoo <span style="color: #009900;">{</span><br />
<br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Foo foo <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Foo<span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
&nbsp;<br />
<br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Foo getInstance<span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span><br />
<br />
<span style="color: #000000; font-weight: bold;">return</span> LazyFoo.<span style="color: #006633;">foo</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
<span style="color: #009900;">}</span></pre>
</div>
</div>
<p>维基百科的DCL解释:</p>
<p>http://en.wikipedia.org/wiki/Double-checked_locking</p>
<p>DCL的完美解决方案:</p>
<p>http://www.theserverside.com/patterns/thread.tss?thread_id=39606</p>
<p>总结:<br />
多线程编程, 针对有写操作的变量, 必须 保证其所有引用点与主存中数据一致(考虑采用同步或volatile) .</p>
<p>原文链接：<a href="http://kenwublog.com/explain-java-memory-model-in-detail" rel="bookmark">http://kenwublog.com/explain-java-memory-model-in-detail</a></p>
<img src ="http://www.blogjava.net/zhuofan/aggbug/308754.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuofan/" target="_blank">卓凡</a> 2010-01-08 17:58 <a href="http://www.blogjava.net/zhuofan/articles/308754.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)volatile原理与技巧</title><link>http://www.blogjava.net/zhuofan/articles/308753.html</link><dc:creator>卓凡</dc:creator><author>卓凡</author><pubDate>Fri, 08 Jan 2010 09:57:00 GMT</pubDate><guid>http://www.blogjava.net/zhuofan/articles/308753.html</guid><wfw:comment>http://www.blogjava.net/zhuofan/comments/308753.html</wfw:comment><comments>http://www.blogjava.net/zhuofan/articles/308753.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuofan/comments/commentRss/308753.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuofan/services/trackbacks/308753.html</trackback:ping><description><![CDATA[<p><span style="font-size: 15px; font-weight: bold;"> volatile, 用更低的代价替代同步</span></p>
<p><strong>为什么 使用volatile比同步代价更低? </strong><br />
同步的代价, 主要由其覆盖范围决定, 如果可以降低同步的覆盖范围, 则可以大幅提升程序性能.</p>
<p>而volatile的覆盖范围仅仅变量级别的. 因此它的同步代价很低.</p>
<p><strong>volatile原理是什么?</strong><br />
volatile的语义, 其实是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我.(工作内存详见java内存模型)</p>
<p>因此, 当多核或多线程在访问该变量时, 都将直接 操作 主存, 这从本质上, 做到了变量共享.</p>
<p><strong>volatile的有什么优势? </strong><br />
1, 更大的程序吞吐量<br />
2, 更少的代码实现多线程<br />
3, 程序的伸缩性较好<br />
4, 比较好理解, 无需太高的学习成本</p>
<p><strong>volatile有什么劣势? </strong><br />
1, 容易出问题<br />
2, 比较难设计<br />
</p>
<p><span style="font-size: 15px; font-weight: bold;"> volatile运算存在脏数据问题 </span></p>
<p>volatile仅仅能保证变量可见性, 无法保证原子性.</p>
<p>volatile的race condition示例:</p>
<div class="wp_syntax">
<div class="code">
<pre class="java" style="font-family: monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> TestRaceCondition <span style="color: #009900;">{</span><br />
<br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">volatile</span> <span style="color: #000066; font-weight: bold;">int</span> i <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span><br />
<br />
&nbsp;<br />
<br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> increase<span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span><br />
<br />
i<span style="color: #339933;">++;</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
&nbsp;<br />
<br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">int</span> getValue<span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span><br />
<br />
<span style="color: #000000; font-weight: bold;">return</span> i<span style="color: #339933;">;</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
<span style="color: #009900;">}</span></pre>
</div>
</div>
<p><strong>当多线程执行increase方法时, 是否能保证它的值会是线性递增的呢? </strong><br />
答案是否定的.</p>
<p>原因:<br />
这里的increase方法, 执行的操作是i++, 即 i = i + 1;<br />
针对i = i + 1, 在多线程中的运算, 本身需要改变i的值.<br />
如果, 在i已从内存中取到最新值, 但未与1进行运算, 此时其他线程已数次将运算结果赋值给i.<br />
则当前线程结束时, 之前的数次运算结果都将被覆盖.</p>
<p>即, 执行100次increase, 可能结果是 &lt; 100.<br />
一般来说, 这种情况需要较高的压力与并发情况下, 才会出现.</p>
<p><strong>如何避免这种情况? </strong><br />
解决以上问题的方法:<br />
一种是 操作时, 加上同步.<br />
这种方法, 无疑将大大降低程序性能, 且违背了volatile的初衷.</p>
<p>第二种方式是, 使用硬件原语(CAS), 实现非阻塞算法<br />
从CPU原语上,  支持变量级别的低开销同步.</p>
<p><span style="font-size: 15px; font-weight: bold;"> CPU原语-比较并交换(CompareAndSet),实现非阻塞算法 </span></p>
<p><strong>什么是CAS? </strong><br />
cas是现代CPU提供给并发程序使用的原语操作. 不同的CPU有不同的使用规范.</p>
<p>在 Intel 处理器中，比较并交换通过指令的 cmpxchg 系列实现。<br />
PowerPC 处理器有一对名为&#8220;加载并保留&#8221;和&#8220;条件存储&#8221;的指令，它们实现相同的目地；<br />
MIPS 与 PowerPC 处理器相似，除了第一个指令称为&#8220;加载链接&#8221;。</p>
<p>CAS 操作包含三个操作数 —— 内存位置（V）、预期原值（A）和新值(B)</p>
<p><strong>什么是非阻塞算法? </strong><br />
一个线程的失败或挂起不应该影响其他线程的失败或挂起.这类算法称之为非阻塞(nonblocking)算法</p>
<p>对比阻塞算法:<br />
如果有一类并发操作, 其中一个线程优先得到对象监视器的锁, 当其他线程到达同步边界时, 就会被阻塞.<br />
直到前一个线程释放掉锁后, 才可以继续竞争对象锁.(当然,这里的竞争也可是公平的, 按先来后到的次序)</p>
<p><strong>CAS 原理: </strong></p>
<p>我认为位置 V 应该包含值 A；如果包含该值，则将 B 放到这个位置；否则，不要更改该位置，只告诉我这个位置现在的值即可。</p>
<p>CAS使用示例(jdk 1.5 并发包 AtomicInteger类分析:)</p>
<div class="wp_syntax">
<div class="code">
<pre class="java" style="font-family: monospace;"><span style="color: #008000; font-style: italic; font-weight: bold;">/**<br />
<br />
* Atomically sets to the given value and returns the old value.<br />
<br />
*<br />
<br />
* @param newValue the new value<br />
<br />
* @return the previous value<br />
<br />
*/</span><br />
<br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span> getAndSet<span style="color: #009900;">(</span><span style="color: #000066; font-weight: bold;">int</span> newValue<span style="color: #009900;">)</span> <span style="color: #009900;">{</span><br />
<br />
<span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">(</span><span style="color: #339933;">;;</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span><br />
<br />
<span style="color: #000066; font-weight: bold;">int</span> current <span style="color: #339933;">=</span> get<span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">(</span>compareAndSet<span style="color: #009900;">(</span>current, newValue<span style="color: #009900;">)</span><span style="color: #009900;">)</span><br />
<br />
<span style="color: #000000; font-weight: bold;">return</span> current<span style="color: #339933;">;</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
<span style="color: #009900;">}</span><br />
<br />
&nbsp;<br />
<br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">boolean</span> compareAndSet<span style="color: #009900;">(</span><span style="color: #000066; font-weight: bold;">int</span> expect, <span style="color: #000066; font-weight: bold;">int</span> update<span style="color: #009900;">)</span> <span style="color: #009900;">{</span><br />
<br />
<span style="color: #000000; font-weight: bold;">return</span> unsafe.<span style="color: #006633;">compareAndSwapInt</span><span style="color: #009900;">(</span><span style="color: #000000; font-weight: bold;">this</span>, valueOffset, expect, update<span style="color: #009900;">)</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #009900;">}</span></pre>
</div>
</div>
<p>这个方法是, AtomicInteger类的常用方法, 作用是, 将变量设置为指定值, 并返回设置前的值.<br />
它利用了cpu原语compareAndSet来保障值的唯一性.</p>
<p>另, AtomicInteger类中, 其他的实用方法, 也是基于同样的实现方式.<br />
比如 getAndIncrement, getAndDecrement, getAndAdd等等.</p>
<p><span style="font-size: 15px; font-weight: bold;"> CAS语义上存在的 &#8221; ABA 问题&#8221;</span></p>
<p><strong>什么是ABA问题?</strong><br />
假设, 第一次读取V地址的A值, 然后通过CAS来判断V地址的值是否仍旧为A, 如果是, 就将B的值写入V地址,覆盖A值.</p>
<p>但是, 语义上, 有一个漏洞, 当第一次读取V的A值, 此时, 内存V的值变为B值, 然后在未执行CAS前, 又变回了A值.<br />
此时, CAS再执行时, 会判断其正确的, 并进行赋值.</p>
<p>这种判断值的方式来断定内存是否被修改过, 针对某些问题, 是不适用的.</p>
<p>为了解决这种问题, jdk 1.5并发包提供了AtomicStampedReference(有标记的原子引用)类, 通过控制变量值的版本来保证CAS正确性.</p>
<p>其实, 大部分通过值的变化来CAS, 已经够用了.</p>
<p><span style="font-size: 15px; font-weight: bold;"> jdk1.5原子包介绍(基于volatile) </span></p>
<p>包的特色:<br />
1, 普通原子数值类型AtomicInteger, AtomicLong提供一些原子操作的加减运算.</p>
<p>2, 使用了解决脏数据问题的经典模式-&#8221;比对后设定&#8221;, 即 查看主存中数据是否与预期提供的值一致,如果一致,才更新.</p>
<p>3, 使用AtomicReference可以实现对所有对象的原子引用及赋值.包括Double与Float,<br />
但不包括对其的计算.浮点的计算,只能依靠同步关键字或Lock接口来实现了.</p>
<p>4, 对数组元素里的对象,符合以上特点的, 也可采用原子操作.包里提供了一些数组原子操作类<br />
AtomicIntegerArray, AtomicLongArray等等.</p>
<p>5, 大幅度提升系统吞吐量及性能.</p>
<p>具体使用, 详解java doc.</p>
<p>原文链接：<a href="http://kenwublog.com/the-theory-of-volatile" rel="bookmark">http://kenwublog.com/the-theory-of-volatile</a></p>
<img src ="http://www.blogjava.net/zhuofan/aggbug/308753.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuofan/" target="_blank">卓凡</a> 2010-01-08 17:57 <a href="http://www.blogjava.net/zhuofan/articles/308753.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>