【volatile概念】
volatile关键字的主要作用是是变量在多个线程间可见。
【注意】
在java中,每一个线程都会有一块工作内存区,其中存放着所有线程共享的主内存中的变量的拷贝。当线程执行时,他在自己的工作内存区中操作这些变量。为了存取一个共享的变量,一个线程通常会先获取并去清除它的内存工作区,把这些共享变量从所有线程的共享内存区中正确地装入到他自己所在的工作内存区中,当线程解锁时保证该内存区中变量的值写回到共享内存中。
一个线程可以执行的操作有:使用(use)、赋值(assign)、装载(load)、存储(store)、锁定(lock)、解锁(unlock)。
而主内存可以执行的操作有:读(read)、写(write)、锁定(lock)、解锁(unlock),每个操作都是原子的。
volatile的作用就是强制线程到主内存(共享内存)里去读取变量,而不去线程工作内存区里去读取,从而实现了多个线程间的变量可见。也就是满足线程安全的可见性。
【线程执行流程图】
【volatile可见性的例子】
package com.higgin.part6; public class MyThread extends Thread{ /** * 加与不加volatile * 不加volatile:main线程中将isRunning设置为flase,MyThread线程中的isRunning不会改变 * 加上volatile:main线程中将isRunning设置为flase,MyThread线程中的isRunning会随之改变 */ private volatile boolean isRunning = true; private void setRunning(boolean isRunning){ this.isRunning = isRunning; } @Override public void run(){ System.out.println("进入run()方法..."); while(isRunning){ //如果MyThread线程的isRunning不改为false,线程会永远卡在这里 } System.out.println("线程停止!!!"); } public static void main(String[] args) throws InterruptedException { MyThread mt = new MyThread(); mt.start(); Thread.sleep(1000); mt.setRunning(false); System.out.println("isRunning的值已经设置为false..."); } }
【运行结果:不加volatile,MyThread线程一直没有结束】
【运行结果:加上volatile,Main线程改变了isRunning,MyThread线程中的isRunning随之改变,MyThread线程可以停止】
【volatile只有可见性,没有原子性的验证例子】
package com.higgin.part6; import java.util.concurrent.atomic.AtomicInteger; /** * volatile关键字具备可见性,但不具备synchronized关键字的原子性(同步) */ public class VolatileThread extends Thread{ /** * 使用volatile int 和 AtomicInteger的区别 * 使用volatile int :只有可见性,没有原子性 * 使用 AtomicInteger:原子性,确保最终的结果必为50000 */ private static volatile int count; // private static AtomicInteger count = new AtomicInteger(0); //初始化为0 private static void addCount(){ for(int i=0; i<1000; i++){ count++; // count.incrementAndGet(); } System.out.println("count="+count); //for循环结束后,打印count的值 } @Override public void run(){ addCount(); } public static void main(String[] args) { VolatileThread[] threads = new VolatileThread[50]; //100个线程 for(int i=0; i<50; i++){ threads[i] = new VolatileThread(); //为数组中的每个元素初始化对象 } for(int i=0; i<50; i++){ threads[i].start(); } } }
【运行结果,使用volatile,多运行几遍会出现错误】
【运行结果:使用AtomicInteger】
【运行结果:使用AtomicInteger,虽然中间有可能会出现数据错乱的问题,但最终结果必定为50000】
【多个addAndGet在一个方法内是非原子性的,需要加synchronized修饰,保证4个addAndGet的原子性】
package com.higgin.part6; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class AtomicDemo { private static AtomicInteger count = new AtomicInteger(0); /** * 加和不加synchronized * 多个addAndGet在一个方法内是非原子性的,需要加synchronized修饰,保证4个addAndGet的原子性 */ public synchronized int multiAdd(){ count.addAndGet(1); count.addAndGet(2); count.addAndGet(3); count.addAndGet(4); //count=count+1+2+3+4=count+10; return count.get(); //返回count的值 } public static void main(String[] args) { final AtomicDemo ad = new AtomicDemo(); List<Thread> tList = new ArrayList<Thread>(100); for(int i=0; i<100; i++){ tList.add(new Thread(new Runnable() { @Override public void run() { System.out.println("count=="+ad.multiAdd()); } })); } for(Thread t : tList){ t.start(); } } }
【运行结果:不加synchronized,虽然可以确保最后一次结果为1000,但是中途有可能不是10的倍数】
【运行结果:加上synchronized修饰】