在java中,每一个线程都一个独立的工作内存,各个线程的工作内存之间相互独立、互不可见,工作内存中的变量的数据值都是事先从主内存中拷贝的,当线程执行时候,就会在工作内存中操作这些变量的值,最后当线程执行完毕,就会将这些变量的值推送给主内存。
对于volatile修饰的变量而言, 只要在任何工作内存中发生改变就会被强制刷新到主内存中去,然后其他线程的工作内存重新在主内存中获取该变量的值。
volatile使得线程之间数据可见
首先看下面的案例:
package com.wuk.diryRead;
public class MyThread001 extends Thread{
private boolean isRunning=true;
private void setRunning(boolean isRunning) {
this.isRunning=isRunning;
}
@Override
public void run() {
System.out.println("进入run方法。。。。");
while(isRunning) {
}
System.out.println("线程停止");
}
public static void main(String[] args) {
MyThread001 t1=new MyThread001();
t1.start();
try {
Thread.sleep(3000);
t1.setRunning(false);
System.out.println("主线程已经将isRunning设置成false了");
Thread.sleep(1000);
System.out.println(t1.isRunning);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果
进入run方法。。。。
主线程已经将isRunning设置成false了
false
当main方法已经将判断条件isRunning变量的值变成了false,但是在t1线程的工作内存中isRunning的值还是true,所以线程肯定还会一直执行下去。
当我将变量写成如下:
private volatile boolean isRunning=true;
运行结果如下:
进入run方法。。。。
主线程已经将isRunning设置成false了
线程停止
false`
volatile只具备可见性 而不具备synchronized的原子性
案例:
public class MyThread003 extends Thread{
private static volatile int count;
//private static AtomicInteger count=new AtomicInteger(0);
private static void addCount() {
for(int i=0;i<1000;i++) {
count++;
//count.incrementAndGet();
}
System.out.println(count);
}
@Override
public void run() {
addCount();
}
public static void main(String[] args) {
MyThread003[] arr=new MyThread003[10];
for(int i=0;i<arr.length;i++) {
arr[i]=new MyThread003();
}
for(int i=0;i<10;i++) {
arr[i].start();
}
}
}
打印结果
4078
4078
4078
4078
4078
5078
6126
8365
7903
6832
咱们不看前面的值怎么样,因为Java的打印函数具有一定的延迟性, 只看最后一个值,如果volatile具有原子性,那么最后一个结果应当是10000,因为volatile如果在对volatile修饰的数据进行操作时候,如果具备原子性的话,此时对它进行操作的线程只能有一个,这样10个线程累计下来最后一个数据应当是10000,而现在显然不是,那就证明中间有多线程同时操作这个变量,导致最后结果非10000.
如果我们想保证数据的原子性,那么采用上面注释的AtomicInteger 类,它具有原子性,可以使得线程同步操作数据。
注意:AtomicInteger 的多个addAndGet()在同一个方法内是非原子性,需要加上synchronized修饰。
案例
private static void addCount() {
for(int i=0;i<1000;i++) {
count.addAndGet(1);
count.addAndGet(2);
count.addAndGet(3);
count.addAndGet(4);
}
要想保证每次加10那么久需要给该方法加上一个synchronized。
所以综上,我们可以得到volatile的结论是:volatile修饰的数据在线程之间具备可见性,但是他不具备数据的原子性。