这个例子很简单,新建的线程里有一个普通变量stop,用来表示是否结束循环里的自增操作。主线程启动这个线程后,将该变量置为true,观察线程
是否打印出finish loop那行,如果存在可见性问题,主线程修改stop值为true,线程v看stop的值应该还是false。
<code>01</code>
<code>class</code> <code>visibilitythread</code><code>extends</code> <code>thread {</code>
<code>02</code>
<code> </code><code>private</code> <code>boolean</code> <code>stop;</code>
<code>03</code>
<code>04</code>
<code> </code><code>public</code> <code>void</code> <code>run() {</code>
<code>05</code>
<code> </code><code>int</code> <code>i =</code><code>0</code><code>;</code>
<code>06</code>
<code> </code><code>system.out.println(</code><code>"start loop."</code><code>);</code>
<code>07</code>
<code> </code><code>while</code><code>(!getstop()) {</code>
<code>08</code>
<code> </code><code>i++;</code>
<code>09</code>
<code> </code><code>}</code>
<code>10</code>
<code> </code><code>system.out.println(</code><code>"finish loop,i="</code> <code>+ i);</code>
<code>11</code>
<code> </code><code>}</code>
<code>12</code>
<code>13</code>
<code> </code><code>public</code> <code>void</code> <code>stopit() {</code>
<code>14</code>
<code> </code><code>stop =</code><code>true</code><code>;</code>
<code>15</code>
<code>16</code>
<code>17</code>
<code> </code><code>public</code> <code>boolean</code> <code>getstop(){</code>
<code>18</code>
<code> </code><code>return</code> <code>stop;</code>
<code>19</code>
<code>20</code>
<code>}</code>
<code>21</code>
<code>22</code>
<code>public</code> <code>class</code> <code>visibilitytest {</code>
<code>23</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>main(string[] args)</code><code>throws</code> <code>exception {</code>
<code>24</code>
<code> </code><code>visibilitythread v =</code><code>new</code> <code>visibilitythread();</code>
<code>25</code>
<code> </code><code>v.start();</code>
<code>26</code>
<code>27</code>
<code> </code><code>thread.sleep(</code><code>1000</code><code>);</code><code>//停顿1秒等待新启线程执行</code>
<code>28</code>
<code> </code><code>system.out.println(</code><code>"即将置stop值为true"</code><code>);</code>
<code>29</code>
<code> </code><code>v.stopit();</code>
<code>30</code>
<code> </code><code>thread.sleep(</code><code>1000</code><code>);</code>
<code>31</code>
<code> </code><code>system.out.println(</code><code>"finish main"</code><code>);</code>
<code>32</code>
<code> </code><code>system.out.println(</code><code>"main中通过getstop获取的stop值:"</code> <code>+ v.getstop());</code>
<code>33</code>
<code>34</code>
我们先来执行一遍(操作系统:xp,下同。jdk:见图示):

执行结果如上图,有人该问了,线程v最终停下来了,这不是表示它看到stop值为true了吗?是的,确实如此。但让我们再看一个这个程序的执行结果。
这一次,我们发现程序一直未能结束,表示线程v看到stop的值是false,但是主线程打印出的值却是true。
对比两次的执行方式,我们发现后一次加上了-server选项。显示version的时候也由client vm变成了server
vm。那么client vm与server vm有什么区别在哪里?简单地讲,client
vm启动时做了一般优化,耗时少,启动快,但程序执行的也相对也较慢;server
vm启动的时候做了更多优化,耗时多,启动慢,但程序执行快。如果在运行java命令的时候没有指定具体模式的时候,会有一个默认值,这个默认值随硬件和
我们再来看个例子,这个例子源于hotspot vm的一个bug:
<code>public</code> <code>class</code> <code>interruptedvisibilitytest {</code>
<code> </code><code>public</code> <code>void</code> <code>think() {</code>
<code> </code><code>system.out.println(</code><code>"新线程正在执行"</code><code>);</code>
<code> </code><code>while</code> <code>(</code><code>true</code><code>) {</code>
<code> </code><code>if</code> <code>(checkinterruptedstatus())</code><code>break</code><code>;</code>
<code> </code><code>system.out.println(</code><code>"新线程退出循环"</code><code>);</code>
<code> </code><code>private</code> <code>boolean</code> <code>checkinterruptedstatus() {</code>
<code> </code><code>return</code> <code>thread.currentthread().isinterrupted();</code>
<code> </code><code>final</code> <code>interruptedvisibilitytest test =</code><code>new</code> <code>interruptedvisibilitytest();</code>
<code> </code><code>thread thinkerthread =</code><code>new</code> <code>thread(</code><code>"thinker"</code><code>) {</code>
<code> </code><code>public</code> <code>void</code> <code>run() {</code>
<code> </code><code>test.think();</code>
<code> </code><code>}</code>
<code> </code><code>};</code>
<code> </code><code>thinkerthread.start();</code>
<code> </code><code>thread.sleep(</code><code>1000</code><code>);</code><code>//等待新线程执行</code>
<code> </code><code>system.out.println(</code><code>"马上中断thinkerthread"</code><code>);</code>
<code> </code><code>thinkerthread.interrupt();</code>
<code> </code><code>system.out.println(</code><code>"已经中断thinkerthread"</code><code>);</code>
<code> </code><code>thinkerthread.join(</code><code>3000</code><code>);</code>
<code> </code><code>if</code> <code>(thinkerthread.isalive()) {</code>
<code> </code><code>system.err.println(</code><code>"thinkerthread未能在中断后3s停止"</code><code>);</code>
<code> </code><code>system.err.println(</code><code>"jmv bug"</code><code>);</code>
<code> </code><code>system.err.println(</code><code>"主线程中检测thinkerthread的中断状态:"</code> <code>+ thinkerthread.isinterrupted());</code>
这个例子也很简单,thinkerthread一直检查中断状态,主线程在启动thinkerthread之后的某个时刻调用interrupt中
thinkerthread没能退出循环,没看到主线程所置的中断状态。
后面这个例子是hotpost vm的一个bug导致的,在最新的hotspot中应该已经被修复了(笔者未测试最新版)。其它vm如ibm
j9,jrockit,harmony等并没有发现这样的bug。说这是bug,是因为jls中规定了main发出的中断必须对
thinkerthread可见。但是,如第一个例子,则不是bug,因为jls是允许这种行为的。当在第一个例子的循环中的i++后面加上一句
jls允许未充分同步的代码出现可见性问题,但是某个实际的jvm完全可以实现的比jls上规定的更强,比如不允许可见性问题出现,那么,在这样的
jvm上就展现不出这样的问题了。第一个例子这里只是运行在hotpost下,也许在其它jvm下同样采用最优化的方式执行,可能并不会出现这里的问题。
在我们编码的时候,也许并不知道代码会跑在什么样的系统上,不知道会采用什么样的jvm,为了使得写出的代码更健壮,我们只能按照规范所规定的最低
保证去编码,要避免这类问题,只有保证代码充分同步,避免数据争用,而不应该依赖于某个具体jvm实现。即使是具体的某款jvm,不同的版本间也可能存在
着差异。
最后,这样的例子启发我们,测试代码的时候应尽可能启用各jvm的最佳优化模式。
至此,我们已经了解到实际中多线程运行真的会出现这样的场景。<b>为什么会出现可见性问题?有什么解决方案?</b>下面链接中的内容为我们提供了专业的解答。
<a href="http://ifeve.com/volatile/">http://ifeve.com/volatile/</a>
<a href="http://ifeve.com/java-memory-model-0/">http://ifeve.com/java-memory-model-0/</a>
<a href="http://ifeve.com/java-concurrency-constructs/">http://ifeve.com/java-concurrency-constructs/</a>
<a href="http://bbs.csdn.net/topics/390348019">csdn上有人发的一个真实案例</a>