Java Synchronize下的volatile关键字详解
简介关键词:Synchronize与volatile
- Synchronize:无论是对于Synchronize同步方法异或是Synchronize块,本质是对某对象或某类加锁,让多线程进行队列化的有序地同步执行。
- volatile:用于修饰变量。在多线程执行过程中,禁止线程从工作内存(缓存)中读取值。
volatile问题抛出:
让我们看到这样一个问题,我们设置一个含有boolean标志位的类Test,以及两个Runable接口实例,分别为MyThread1,MyThread2。
在MyThread1中通过while循环判断flag是否更改,如果更改便结束循环退出。
在MyThread2中改变flag值。
代码如下:
Test:
publicclassTest{
booleanflag=true;
}
MyThread1:
publicclassMyThread1implementsRunnable{
Testtest;
publicMyThread1(Testtest){
this.test=test;
}
@Override
publicvoidrun(){
while(test.flag){
}
System.out.println(Thread.currentThread().getName()+"我已退出");
}
}
MyThread2:
publicclassMyThread2implementsRunnable{
Testtest;
publicMyThread2(Testtest){
this.test=test;
}
@Override
publicvoidrun(){
try{
Thread.sleep(3000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
test.flag=false;
}
}
main函数:
publicstaticvoidmain(String[]args){
Testtest=newTest();
MyThread1myThread1=newMyThread1(test);
MyThread2myThread2=newMyThread2(test);
Threadthread1=newThread(myThread1);
Threadthread2=newThread(myThread2);
thread1.start();
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
thread2.start();
}
- 按照我们常规的想法,在在Sleep延时之后,Thread2会更改flag的值。而Thread1也会因此退出循环。
- 但实际上,Thread1并没有因此退出循环。
- 原因是Thread1并未从内存中读取flag,而是直接从工作内存中读取。所以即便是Thread2已经更新了flag的值,但Thread1工作内存中的flag也并未更新。所以便导致了Thread1陷入死循环。
解决方法:
那么如何解决这样的问题呢?
很简单,使用volatile关键字。让线程不得不从主内存中读取flag值。
volatilebooleanflag=true;
在我们添加volatile关键字后,Thread1便可以正常退出。
在Synchronize下的volatile:
此时我们已经了解了volatile关键字的作用,那么在我们的volatile关键字中,Synchronize有着怎样的作用呢?
volatile问题抛出:
其实在我们实际使用中,volatile其实也是有一些隐患的。
例如:我们创造10条线程,每条线程都使volatile修饰的int常量增加1000000次。
publicclassMyThread1implementsRunnable{
volatileintnum=0;
@Override
publicvoidrun(){
for(inti=0;i<1000000;i++){
num++;
System.out.println(Thread.currentThread().getName()+""+num);
}
}
publicstaticvoidmain(String[]args){
MyThread1myThread1=newMyThread1();
Thread[]arr=newThread[10];
for(inti=0;i<10;i++){
arr[i]=newThread(myThread1);
}
for(inti=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的情况下,通过比较原值与当前值,不但性能高效,并且也能达到线程安全的目的。
到此这篇关于Synchronize下的volatile关键字的文章就介绍到这了,更多相关Synchronizevolatile关键字内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。