温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Java Synchronize下的volatile关键字是什么

发布时间:2021-03-12 17:05:42 来源:亿速云 阅读:231 作者:TREX 栏目:开发技术

这篇文章主要讲解了“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陷入死循环。

Java Synchronize下的volatile关键字是什么 

解决方法:

那么如何解决这样的问题呢?
很简单,使用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();
  }

 }

}

Java Synchronize下的volatile关键字是什么

分析:

  • 从结果中,我们可以看到,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关键字是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI