Java Synchronize下的volatile关键字是什么
这篇文章主要讲解了“Java Synchronize下的volatile关键字是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“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的情况下,通过比较原值与当前值,不但性能高效,并且也能达到线程安全的目的。
感谢各位的阅读,以上就是“Java Synchronize下的volatile关键字是什么”的内容了,经过本文的学习后,相信大家对Java Synchronize下的volatile关键字是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是恰卡编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!
推荐阅读
-
在Python中,将K添加到列元组列表中的最小元素
处理数据集涉及识别特定列中的最小值并通过添加常量值(K)来更新它。通过实施优化的解决方案,我们可以有效地执行此操作,这对于数据...
-
使用switch case语句编写的C程序,用于计算几何图形的面积
#includevoidmain(){intfig_code;floatside,base,length,...
-
如何使 C# 代码可重用?
要在C#中使代码可重用,请使用接口。接口定义属性、方法和事件,这些成员是接口的成员。接口只包含成员的声明。派生类负责定义成员。这通...
-
C# 中的覆盖和隐藏有什么区别?
方法隐藏在C#中也称为隐藏。父类的方法可供子类使用,无需在遮蔽中使用override关键字。子类有其自己版本的相同函数。在...
-
在Java中使用示例双倍longValue()函数
Java是一种强大的面向对象语言,可以对各种数据类型进行高度的控制和精确度。其中一种功能是doublelongValue(),...
-
如何在Java中定义JSON字段名称的命名约定?
TheFieldNamingPolicycanbeusedtodefineafewstandardnaming...
-
Servlet中的HttpSession接口
在JavaWeb开发领域,了解HttpSession接口是创建动态和响应式Web应用程序的关键。在本文中,我们将探讨...
-
使用while循环查找自然数之和的Java程序
自然数之和可以使用编程语言中的不同迭代语句来计算。迭代语句是执行一组特定代码行直到循环语句中的条件失败的语句。在本文中,我们将讨论...
-
我们可以将Java数组转换为列表吗?
我们可以使用Arrays.asList()方法轻松地将Java数组转换为List。语法publicstaticLi...
-
Java中如何在不使用任何外部库的情况下读取网页内容?
TheURLclassofthejava.netpackagerepresentsaUniformResour...