对java中volatile关键字的理解
2016-10-14 00:00
239 查看
volatile关键字的作用:保证变量的可见性
一定要谨记
volatile作用的具体描述:
引自- wangzzu的博客文章
上面这段话能十分清楚的说明这个变量的用处了吧。下面配合代码更加准确的验证这个问题。
代码引自-- 阿里工程师oldratlee的github
Demo类
出现上述问题说明的代码:
例子中主线程的stop变量没有做任何的显示定义volatile。
当我们执行后的打印结果为:
并没有打印
并且程序还没有结束。
接下来我们对stop变量添加volatile
程序完美结束。也印证了上文描述中的内容。
组合状态读到无效组合
--此时使用volatile则无法解决同时修改两个值,数据还是返回正确的问题。
意思是同时对两个值修改,但是实际返回确实错误的。
程序设计时,会需要多个状态记录(状态可以是个
无效组合 是指 从来没有设置过的组合。
Demo类
实际测试代码
无效值 是指 从来没有设置过的值。
Demo类
DEMO:
对于单个变量的值修改 给变量添加volatile也是可以解决此问题的。
十分感谢
Matt's Blog
李鼎(哲良)
一定要谨记
volatile关键字在 Java 代码中仅仅保证这个变量是可见的:它不保证原子性。在那些非原子且可由多个线程访问的易变操作中,一定不能够依赖于 volatile 的同步机制。相反,要使用
java.util.concurrent包的同步语句、锁类和原子类。它们在设计上能够保证程序是线程安全的。
volatile作用的具体描述:
Java如何保证可见性
Java提供了volatile关键字来保证可见性。当使用volatile修饰某个变量时,它会保证对该变量的修改会立即被更新到内存中,并且将其它缓存中对该变量的缓存设置成无效,因此其它线程需要读取该值时必须从主内存中读取,从而得到最新的值。
引自- wangzzu的博客文章
上面这段话能十分清楚的说明这个变量的用处了吧。下面配合代码更加准确的验证这个问题。
代码引自-- 阿里工程师oldratlee的github
Demo类
com.oldratlee.fucking.concurrency.NoPublishDemo。
Demo说明
主线程中设置属性stop为
true,以控制在
main启动的任务线程退出。
问题说明
在主线程属性stop为
true后,但任务线程持续运行,即任务线程中一直没有读到新值。
出现上述问题说明的代码:
public class NoPublishDemo { boolean stop = false; public static void main(String[] args) { // LoadMaker.makeLoad(); NoPublishDemo demo = new NoPublishDemo(); Thread thread = new Thread(demo.getConcurrencyCheckTask()); thread.start(); Utils.sleep(1000); System.out.println("Set stop to true in main!"); demo.stop = true; System.out.println("Exit main."); } ConcurrencyCheckTask getConcurrencyCheckTask() { return new ConcurrencyCheckTask(); } private class ConcurrencyCheckTask implements Runnable { @Override public void run() { System.out.println("ConcurrencyCheckTask started!"); // 如果主线中stop的值可见,则循环会退出。 // 在我的开发机上,几乎必现循环不退出!(简单安全的解法:在running属性上加上volatile) while (!stop) { } System.out.println("ConcurrencyCheckTask stopped!"); } } }
例子中主线程的stop变量没有做任何的显示定义volatile。
当我们执行后的打印结果为:
ConcurrencyCheckTask started! Set stop to true in main! Exit main.
并没有打印
ConcurrencyCheckTask stopped!
并且程序还没有结束。
接下来我们对stop变量添加volatile
volatile boolean stop = false;
程序完美结束。也印证了上文描述中的内容。
组合状态读到无效组合
--此时使用volatile则无法解决同时修改两个值,数据还是返回正确的问题。
意思是同时对两个值修改,但是实际返回确实错误的。
程序设计时,会需要多个状态记录(状态可以是个
POJO对象或是
int等等)。常看到多状态读写没有同步的代码,并且写的同学会很自然地就忽略了线程安全的问题。
无效组合 是指 从来没有设置过的组合。
Demo说明
主线程修改多个状态,为了方便检查,每次写入有个固定的关系:第2个状态是第1个状态值的2倍。在任务线程中读取多个状态。Demo类
com.oldratlee.fucking.concurrency.InvalidCombinationStateDemo。
问题说明
任务线程中读到了 第2个状态不是第1个状态值2倍的值,即是无效值。快速运行
mvn compile exec:java -Dexec.mainClass=com.oldratlee.fucking.concurrency.InvalidCombinationStateDemo
实际测试代码
public class InvalidCombinationStateDemo { public static void main(String[] args) { CombinationStatTask task = new CombinationStatTask(); Thread thread = new Thread(task); thread.start(); Random random = new Random(); while (true) { int rand = random.nextInt(1000); task.state1 = rand; task.state2 = rand * 2; } } private static class CombinationStatTask implements Runnable { // 对于组合状态,加 volatile 不能解决问题 volatile int state1; volatile int state2; @Override public void run() { int c = 0; for (long i = 0; ; i++) { int i1 = state1; int i2 = state2; if (i1 * 2 != i2) { c++; System.err.printf("Fuck! Got invalid CombinationStat!! check time=%s, happen time=%s(%s%%), count value=%s|%s\n", i + 1, c, (float) c / (i + 1) * 100, i1, i2); } else { // 如果去掉这个输出,则在我的开发机上,发生无效组合的概率由 ~5% 降到 ~0.1% System.out.printf("Emm... %s|%s\n", i1, i2); } } } } }
long
变量读到无效值
无效值 是指 从来没有设置过的值。long变量读写不是原子的,会分为2次4字节操作。
Demo类
com.oldratlee.fucking.concurrency.InvalidLongDemo。
Demo说明
主线程修改long变量,为了方便检查,每次写入的
long值的高4字节和低4字节是一样的。在任务线程中读取
long变量。
问题说明
任务线程中读到了高4字节和低4字节不一样的long变量,即是无效值。
快速运行
mvn compile exec:java -Dexec.mainClass=com.oldratlee.fucking.concurrency.InvalidLongDemo
DEMO:
public class InvalidLongDemo { long count = 0; public static void main(String[] args) { // LoadMaker.makeLoad(); InvalidLongDemo demo = new InvalidLongDemo(); Thread thread = new Thread(demo.getConcurrencyCheckTask()); thread.start(); for (int i = 0; ; i++) { long l = i; demo.count = l << 32 | l; } } ConcurrencyCheckTask getConcurrencyCheckTask() { return new ConcurrencyCheckTask(); } private class ConcurrencyCheckTask implements Runnable { @Override public void run() { int c = 0; for (int i = 0; ; i++) { long l = count; long high = l >>> 32; long low = l & 0xFFFFFFFFL; if (high != low) { c++; System.err.printf("Fuck! Got invalid long!! check time=%s, happen time=%s(%s%%), count value=%s|%s\n", i + 1, c, (float) c / (i + 1) * 100, high, low); } else { // 如果去掉这个输出,则在我的开发机上没有观察到invalid long System.out.printf("Emm... %s|%s\n", high, low); } } } } }
对于单个变量的值修改 给变量添加volatile也是可以解决此问题的。
十分感谢
Matt's Blog
李鼎(哲良)
相关文章推荐
- 理解java中的volatile关键字
- Java线程工作内存与主内存变量交换过程及volatile关键字理解
- Java线程工作内存与主内存变量交换过程及volatile关键字理解
- 理解Java关键字volatile
- 深入理解Java并发机制(2)--volatile关键字
- 深入理解Java并发机制(2)--volatile关键字
- 我是菜鸟:java多线程join方法,volatile以及synchronized关键字理解
- java中volatile关键字的理解
- 深入理解Java并发机制(2)--volatile关键字
- 谈谈Java中Volatile关键字的理解
- 【Java基础】java中常用关键字理解this、static、final、volatile理解
- java中volatile关键字的理解
- 深入理解Java并发机制(2)--volatile关键字
- Java:volatile 关键字的一点理解
- 关于Java中的关键字volatile的理解
- java:深入理解volatile关键字
- 对于java关键字volatile,和transient 的理解
- 深入理解Java并发机制(2)--volatile关键字
- 深入理解Java并发机制(2)--volatile关键字
- Java 关键字 volatile 的理解与正确使用