这篇文章主要讲解了“java synchronize下的volatile关键字是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java synchronize下的volatile关键字是什么”吧!
简介关键词:synchronize与volatile
-
synchronize:无论是对于synchronize同步方法异或是synchronize块,本质是对某对象或某类加锁,让多线程进行队列化的有序地同步执行。
-
volatile:用于修饰变量。在多线程执行过程中,禁止线程从工作内存(缓存)中读取值。
volatile问题抛出:
让我们看到这样一个问题,我们设置一个含有boolean标志位的类test,以及两个runable接口实例,分别为mythread1,mythread2。
在mythread1中通过while循环判断flag是否更改,如果更改便结束循环退出。
在mythread2中改变flag值。
代码如下:
test:
public class test { boolean flag = true; }
mythread1:
public class mythread1 implements runnable{ test test; public mythread1(test test){ this.test = test; } @override public void run() { while (test.flag){ } system.out.println(thread.currentthread().getname() " 我已退出"); } }
mythread2:
public class mythread2 implements runnable{ test test; public mythread2(test test){ this.test = test; } @override public void run() { try { thread.sleep(3000); } catch (interruptedexception e) { e.printstacktrace(); } test.flag = false; } }
main函数:
public static void main(string[] args) { test test = new test(); mythread1 mythread1 = new mythread1(test); mythread2 mythread2 = new mythread2(test); thread thread1 = new thread(mythread1); thread thread2 = new thread(mythread2); thread1.start(); try { thread.sleep(1000); } catch (interruptedexception e) { e.printstacktrace(); } thread2.start(); }
-
按照我们常规的想法,在在sleep延时之后,thread2会更改flag的值。而thread1也会因此退出循环。
-
但实际上,thread1并没有因此退出循环。
-
原因是thread1并未从内存中读取flag,而是直接从工作内存中读取。所以即便是thread2已经更新了flag的值,但thread1工作内存中的flag也并未更新。所以便导致了thread1陷入死循环。
解决方法:
那么如何解决这样的问题呢?
很简单,使用volatile关键字。让线程不得不从主内存中读取flag值。
volatile boolean flag = true;
在我们添加volatile关键字后,thread1便可以正常退出。
在synchronize下的volatile:
此时我们已经了解了volatile关键字的作用,那么在我们的volatile关键字中,synchronize有着怎样的作用呢?
volatile问题抛出:
其实在我们实际使用中,volatile其实也是有一些隐患的。
例如:我们创造10条线程,每条线程都使volatile修饰的int常量增加1000000次。
public class mythread1 implements runnable{ volatile int num = 0; @override public void run() { for (int i = 0; i < 1000000; i ) { num ; system.out.println(thread.currentthread().getname() " " num); } } public static void main(string[] args) { mythread1 mythread1 = new mythread1(); thread[] arr = new thread[10]; for (int i = 0; i < 10; i ) { arr[i] = new thread(mythread1); } for (int i = 0; i < 10; i ) { arr[i].start(); } } }
分析:
-
从结果中,我们可以看到,num并没有像我们想象一样达到10000000。
-
这是因为volatile所修饰的int变量在自加过程中并非原子操作。这也就是说这个自加的过程可以被打断。可以被分解为:获取值,自加,赋值三个步骤。
-
例如当,num = 0时,thread1获取了num的值,并赋值为1,但此时在thread1还未来得及更新线程的时候,thread的2以及thread3已经将线程的值更新为2,但thread1再赋值,num的值又会重新变为1。
-
所以,我们便需要在自加的过程中添加synchronize关键字,让线程实现同步。
结论:
在我们使用volatile关键字时,需要注意操作是否为原子操作,以免造成线程不安全。
扩展:
其实,对于原子操作,java已经提供了atomic原子类来解决。其中涉及了cas机制,在不使用synchronize的情况下,通过比较原值与当前值,不但性能高效,并且也能达到线程安全的目的。
感谢各位的阅读,以上就是“java synchronize下的volatile关键字是什么”的内容了,经过本文的学习后,相信大家对java synchronize下的volatile关键字是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是本站,小编将为大家推送更多相关知识点的文章,欢迎关注!