Java多线程编程——volatile关键字
Posted 猫咪大王
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程编程——volatile关键字相关的知识,希望对你有一定的参考价值。
(本篇主要内容摘自《Java多线程编程核心技术》)
volatile关键字的主要作用是保证线程之间变量的可见性。
package com.func; public class RunThread extends Thread{ private boolean isRunning = true; // volatile 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("停止运行了!"); } }
package com.test; import com.func.RunThread; public class Test3 { public static void main(String[] args){ try { RunThread runThread = new RunThread(); runThread.start(); Thread.sleep(1000); runThread.setRunning(false); System.out.println("isRunning已经赋值为false"); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } }
将JVM设置为-server时就会出现下面状况
可以看到 System.out.println("停止运行了!"); 没有得到运行,也就是说isRunning 一直是true的状态。 runThread.setRunning(false); 这句话并没有起到相应的作用。
那么为什么会这样呢?
原因就在于私有堆栈和公共堆栈中的值不一致造成的。当把JVM设置成-server的方式,为了执行效率,线程会一直在私有堆栈中取值。 runThread.setRunning(false);将公共堆栈中的isRunning设置成为false,但是私有堆栈中的isRunning 并没有得到同步。
面对这种情况就可以采用volatile关键字解决。将isRunniing变量使用volatile关键字修饰。
volatile private boolean isRunning = true;
volatile关键字强制线程从公共堆栈中获取变量值,而不是从私有堆栈中获取变量值。
使用volatile关键字可以增加实力变量在多个线程之间的可见性。但是volatile关键字只是保证可见性并不保证原子性。不能使用volatile关键字来保证线程安全。
下面的例子会说明volatile关键字不能保证线程安全。
package com.func; public class MyThread extends Thread{ volatile public static int count; // public static int count; private static void addCount(){ // synchronized private static void addCount(){ for (int i = 0; i < 100; i++) { count ++; } System.out.println("count = " + count); } @Override public void run(){ addCount(); } }
package com.test; import com.func.MyThread; public class Test { public static void main(String[] args){ try { MyThread[] myThreads = new MyThread[100]; for (int i = 0; i < 100; i++) { myThreads[i] = new MyThread(); } for (int i = 0; i < myThreads.length; i++) { myThreads[i].start(); } } catch (Exception e) { // TODO: handle exception } } }
可以看见最后的结果并不是10000,看见volatile并不能保证线程安全。虽然数据都是从公共堆栈取出来的,但是数据的取出来之后的修改并不是原子操作(count++这样的操作并不是原子的),所以最后将修改后的数据进行同步时,数据并不是我们想要的。
为了保证数据的原子性,我们还是需要使用synchronized关键字。synchronized关键字保证了原子性,解决了多个线程之间访问资源的同步性。
synchronized private static void addCount(){
...
}
以上是关于Java多线程编程——volatile关键字的主要内容,如果未能解决你的问题,请参考以下文章
Java 并发编程 -- 并发编程线程基础(线程安全问题可见性问题synchronized / volatile 关键字CASUnsafe指令重排序伪共享Java锁的概述)
Java 并发编程 -- 并发编程线程基础(线程安全问题可见性问题synchronized / volatile 关键字CASUnsafe指令重排序伪共享Java锁的概述)