一、为什么使用volatile

首先,通过一段简单的代码来理解为什么要使用volatile:

 public class RunThread extends Thread{
private boolean isRunning = true;
public boolean isRunning(){
return isRunning;
} public void setRunning(boolean isRunning){
this.isRunning = isRunning;
} @Override
public void run() {
System.out.println("进入run...");
while(isRunning==true){}
System.out.println("线程停止了");
}
} import java.util.concurrent.TimeUnit; public class Run {
public static void main(String[] args){
try{
RunThread thread = new RunThread();
thread.start();
TimeUnit.MILLISECONDS.sleep(1000);
thread.setRunning(false);
System.out.println("赋值为false");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

执行结果:

我们创建了一个RunThread对象,并启动了该线程。当 isRunning == True时,将一直进行while循环。可以看到,我们启动了线程之后就调用setRunning方法将isRunning设为false。

按道理来说,当isRunning的状态为false时,就会退出while循环,输出“赋值为false”。然后run方法执行结束,线程执行完毕,整个程序运行结束。

但通过执行结果发现,线程进入了while死循环,线程从未终止。

这是由于在启动RunThread.java线程时,变量 private boolean isRunning = True 存在于公共堆栈和线程的私有堆栈中。JVM以server模式运行时,为了提高运行效率,线程一直从私有堆栈中读取isRunning的值为True。而thread.setRunning(false)执行后,是把公共堆栈中的isRunning改为false。所以即便将isRunning改为false,程序依然进入了while死循环。

解决办法:

使用volatile关键字,把  private boolean isRunning = True 改为 volatile private boolean isRunning = True ,再次运行:

volatile的作用是:强制线程从公共堆栈中取变量的值,而非从线程的私有堆栈中取值


二、 volatile的非原子性

1. 虽然volatile关键字增加了实例变量在多个线程之间的可见性,但它不具备同步性,也就是原子性。

 public class MyThread extends Thread{
volatile public static int count;
private static void addCount(){
for(int i =0;i<100;i++){
count++;
}
System.out.println("count= "+count);
} @Override
public void run() {
addCount();
}
} public class Run {
public static void main(String[] args) {
MyThread[] mythreadArray = new MyThread[100];
for (int i = 0; i < 100; i++) {
mythreadArray[i] = new MyThread();
}
for (int i = 0; i < 100; i++) {
mythreadArray[i].start();
}
}
}

执行结果:

volatile保证了线程每次从公共内存中读取变量,保证了同步数据的可见性。但由上述代码可以看出,使用关键字volatile时出现了非线程安全问题。i++ 操作并非一个原子操作,可以分解为:

  (1)从内存读取 i 的值;

  (2)计算 i 的值;

  (3)将 i 的值写入内存

当一个线程正在执行步骤(2)时,另一个线程也可能也在修改 i 值,就会出现脏数据。

2. 解决办法

(1)方法一:使用synchronized关键字

当在 private static void addCount() 前面加上关键字synchronized后 synchronized private static void addCount() ,再次执行:

当时用了synchronized关键字时,就不必再使用volatile关键字了。

(2)方法二:使用原子类进行i++操作

 public class MyThread extends Thread{
private static AtomicInteger count = new AtomicInteger(0);
synchronized private static void addCount(){
for(int i =0;i<100;i++){
count.incrementAndGet();
}
System.out.println("count= "+count);
} @Override
public void run() {
addCount();
}
}

注意:原子类并不完全安全,只有原子类的increamentAndGet( )方法是原子的,不保证方法与方法之间的调用也是原子的。

最新文章

  1. 【代码笔记】iOS-页面调的时候隐藏工具条
  2. 配置oozie4.10+hadoop2.5.2
  3. 详解CSS中:nth-child的用法
  4. 【转】使用:after清除浮动
  5. ASP.NET MVC使用SSI来实现页面静态化
  6. Scalaz(10)- Monad:就是一种函数式编程模式-a design pattern
  7. git 初始化
  8. openstack 中 log模块分析
  9. de.greenrobot.event.EventBusException: Subscriber class dji.midware.a.e already registered to event class
  10. HW6.23
  11. 安装MYSQL出现的问题
  12. 什么是流利语法Fluent Syntax
  13. HDU 1698 Just a Hook 线段树+lazy-target 区间刷新
  14. HTML meta refresh 刷新与跳转(重定向)页面
  15. Linux initramfs说明
  16. java基础4:深入理解final关键字
  17. maven scope使用和理解
  18. Dom4j解析xml内容——(三)
  19. jQuery全能图片滚动插件
  20. 洛谷P2345 奶牛集会

热门文章

  1. Codeforces Round #423 (Div. 2)
  2. TraceWrite waittype
  3. libconfig C++ 学习笔记
  4. php-------面向对象详解
  5. ThinkPHP中前台输出变量
  6. tensorflow一个很好的博客
  7. SpringAop之日志管理
  8. java8的4大核心函数式接口
  9. iOS一个很好的内存检测工具
  10. 【luogu P2279 [HNOI2003]消防局的设立】 题解