Java多线程 之 原子性与可见性(八)

来源:互联网 发布:权力的游戏 夜王 知乎 编辑:程序博客网 时间:2024/05/17 18:42

1.原子性

Java中的原子性,是指:原子操作是不能被线程调度机制中断的;操作一旦开始,它一定会在可能发生的“上下文切换”(即切换到其他线程执行)之前执行完毕。 
但是千万不要认为“原子操作不需要同步控制(这是错误的)”! 
原子性可以应用于除long和double之外的基本类型的简单操作(赋值和返回值)。long和double这种64位的数据类型,在JVM内部是通过两条32位的操作完成的,因此有可能发生上下文切换。如果给long和double变量加上volatile关键字也可以获得原子性。 
但是原子性并不能保证并发的代码的正确性。比如,在多处理器(可能是单处理器但是多核)多线程环境下,一个线程对某一变量的写入操作有可能只是将这种变化存在了CPU缓存中,而其他线程对该变量的访问只是局限在各自的CPU缓存,这样导致了不一致性。因此,还需要使用volatile关键字来保证可见性,将变量的修改直接写入到内存中。 
当然同步(加锁)机制也可以保证这种可见性,将变量的修改直接写到内存中。 
在同一个任务中,可见性问题是不存在的。即该任务对变量的修改,该任务肯定知道。 
但是,当一个属性的值依赖于它之前的值时(如递增操作),一个属性的值依赖于其他域的值的限制,volatile就无法工作了。这里说的不能工作了,应该是指在并发环境下无法保证代码的正确性吧。 
我的理解:原子性+volatitle可以保证并发的正确性。但是,最好编码时还是尽量用 同步加锁 来保证并发的正确性。第一选择应该是synchronized关键字。 
再次声明:在Java中,自增操作不是原子的。 
在《Thinking in java》第四版中文版的682-684页举例详细说明下面两点: 
(1)在java中,对除long和double之外的基本类型的简单操作(赋值、返回值)是原子性的,但是无法保证并发的正确性。 
(2)在java中,递增操作不是原子性的,会引发并发问题。

2.原子类Atomic

在JDK5之后,引入了AtomicInteger、AtomicLong、AtomicReference等原子类。这些类其实是用来构建java.util.concurrent中的类的。也就是说,这些类是JUC实现的基础类,JUC构建在Atomic之上,JUC的并发是用Atomic来实现的。 
我们在写代码时,要首先考虑使用synchronized关键字和Lock对象,而避免使用Atomic类。当涉及到调优时,可以考虑使用Atomic类。Atomic类要比synchronized、Lock更高效。 
下面使用Atomic类来重写EvenGenerator:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> org.fan.learn.thread.share;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.util.Timer;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.util.TimerTask;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.util.concurrent.atomic.AtomicInteger;<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * Created by thinkpad on 2016/6/19. */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">AtomicEvenGenerator</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">IntGenerator</span> {</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> AtomicInteger atomicInteger = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> AtomicInteger(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">next</span>() {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> atomicInteger.addAndGet(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>);    }    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">main</span>(String[] args) {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Timer().schedule(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TimerTask() {            <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() {                System.err.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Aborting"</span>);                System.exit(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);            }        }, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5000</span>);  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//执行5s后自动终止</span>        EvenChecker.test(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> AtomicEvenGenerator());    }}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li></ul>

这里使用Timer,使程序运行5s之后自动终止。 
运行结果如下: 
Press control-c to exit 
Aborting

关于EvenGenerator的其他部分可参考下面两篇博文: 
Java多线程 之 访问共享资源(六) 
Java多线程 之 访问共享资源synchronized、lock(七)

0 0
原创粉丝点击