博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JIT与可见性
阅读量:6480 次
发布时间:2019-06-23

本文共 4204 字,大约阅读时间需要 14 分钟。

 (被墙移到墙内)

 Overview

Many developers of multi-threaded code are familiar with the idea that different threads can have a different view of a value they are holding, this not the only reason a thread might not see a change if it is not made thread safe.  The JIT itself can play a part.

Why do different threads see different values?

When you have multiple threads, they will attempt to minimise how much they will interact e.g. by trying to access the same memory.  To do this they have a separate local copy e.g. in Level 1 cache.  This cache is usually eventually consistent.  I have seen short periods of between one micro-second and up to 10 milli-seconds where two threads see different values.  Eventually the thread is context switched, the cache cleared or updated.  There is no guarantee as to when this will happen but it is almost always much less than a second.

How can the JIT play a part?

The Java Memory Model says there is no guarantee that a field which is not thread safe will ever see an update.  This allows the JIT to make an optimisation where a value only read and not written to is effectively inlined into the code.  This means that even if the cache is updated, the change might not be reflected in the code.

An example

This code will run until a boolean is set to false.
01 <br />
02 static class MyTask implements Runnable {<br />
03     private final int loopTimes;<br />
04     private boolean running = true;<br />
05     boolean stopped = false;</p>
06 <p>    public MyTask(int loopTimes) {<br />
07         this.loopTimes = loopTimes;<br />
08     }</p>
09 <p>    @Override<br />
10     public void run() {<br />
11         try {<br />
12             while (running) {<br />
13                 longCalculation();<br />
14             }<br />
15         } finally {<br />
16             stopped = true;<br />
17         }<br />
18     }</p>
19 <p>    private void longCalculation() {<br />
20         for (int i = 1; i &lt; loopTimes; i++)<br />
21             if (Math.log10(i) &lt; 0)<br />
22                 throw new AssertionError();<br />
23     }<br />
24 }</p>
25 <p>public static void main(String... args) throws InterruptedException {<br />
26     int loopTimes = Integer.parseInt(args[0]);<br />
27     MyTask task = new MyTask(loopTimes);<br />
28     Thread thread = new Thread(task);<br />
29     thread.setDaemon(true);<br />
30     thread.start();<br />
31     TimeUnit.MILLISECONDS.sleep(100);<br />
32     task.running = false;<br />
33     for (int i = 0; i &lt; 200; i++) {<br />
34         TimeUnit.MILLISECONDS.sleep(500);<br />
35         System.out.println("stopped = " + task.stopped);<br />
36         if (task.stopped)<br />
37             break;<br />
38     }<br />
39 }<br />

This code repeatedly performs some work which has no impact on memory. The only difference it makes is how long it takes. By taking longer, it determines whether the code in run() will be optimised before or after running is set to false.

If I run this with 10 or 100 and -XX:+PrintCompilation I see

111    1     java.lang.String::hashCode (55 bytes)112    2     java.lang.String::charAt (29 bytes)135    3     vanilla.java.perfeg.threads.OptimisationMain$MyTask :longCalculation (35 bytes)204    1 % ! vanilla.java.perfeg.threads.OptimisationMain$MyTask :run @ 0 (31 bytes)stopped = falsestopped = falsestopped = falsestopped = false... many deleted ...stopped = falsestopped = falsestopped = falsestopped = falsestopped = false

If I run this with 1000 you can see that the run() hasn’t been compiled and the thread stops

112    1     java.lang.String::hashCode (55 bytes)112    2     java.lang.String::charAt (29 bytes)133    3     vanilla.java.perfeg.threads.OptimisationMain $MyTask::longCalculation (35 bytes)135    1 %   vanilla.java.perfeg.threads.OptimisationMain $MyTask::longCalculation @ 2 (35 bytes)stopped = true

Once the thread has been compiled, the change is never seen even though the thread will have context switched etc. many times.

How to fix this

The simple solution is to make the field volatile.  This will guarantee the field’s value consistent, not just eventually consistent which is what the cache might do for you.

Conclusion

While there are many examples of question like; Why doesn’t my thread stop? The answer has more to do with Java Memory Model which allows the JIT “inline” the fields that it does the hardware and having multiple copies of the data in different caches.

转载地址:http://edzuo.baihongyu.com/

你可能感兴趣的文章
SCALA中的函数式编程
查看>>
Windows删除无效服务
查看>>
将List<int> 转换为用逗号连接为字符串
查看>>
C/C++中extern关键字详解
查看>>
Eclipse 最有用的快捷键
查看>>
K & DN 的前世今生(微软开源命名变革)
查看>>
--@angularJS--angular与BootStrap3的应用
查看>>
I2C驱动程序框架probe道路
查看>>
u3d单词学习plane
查看>>
10款很好用的 jQuery 图片滚动插件
查看>>
Flask服务入门案例
查看>>
ReadWriteLock与ReentrantReadWriteLock
查看>>
Atitit.软件命名空间 包的命名统计 及命名表(2000个名称) 方案java package...
查看>>
新手指导:教你如何查看识别hadoop是32位还是64位
查看>>
Codeforces Round #180 (Div. 2) D. Fish Weight 贪心
查看>>
Gradle sourceCompatibility has no effect to subprojects(转)
查看>>
百度指数分析
查看>>
使用Mkdocs构建你的项目文档
查看>>
深入理解计算机系统(1.1)------Hello World 是如何运行的
查看>>
三分钟读懂TT猫分布式、微服务和集群之路
查看>>