07 volatile关键字的概念
Volatile概念:volatile关键字的主要作用是使变量在多个线程见可见。
Volatile存在原因:
在没有volatile之前是利用加锁机制,从而保证线程安全,数据的一致性,但是这样的效率比较低,因为每次操作只有一个线程执行。
示例:
package edu.sdut.thread01;
public class MyThread09 extends Thread { /**volatile*/ private volatile boolean isRunning = true; private void setRunning(boolean isRunning) { this.isRunning = isRunning; } public void run() { System.out.println("进入run方法。。"); while(isRunning == true) { } System.out.println("线程停止"); } public static void main(String[] args) throws InterruptedException { MyThread09 m = new MyThread09(); m.start(); Thread.sleep(3000); m.setRunning(false); System.out.println("isRunning的值已经被设置了false"); Thread.sleep(1000); System.out.println(m.isRunning); } }
|
加volatile结果: 进入run方法。。 isRunning的值已经被设置了false 线程停止 False 不加volatile结果: 进入run方法。。 isRunning的值已经被设置了false False |
Volatile原理:
在java中,每一个线程都会有一块工作内存区域,其中存放着所有线程共享的主内存中的变量值得拷贝,当线程执行时,它在自己得工作内存区中操作这些变量。为了存放一个共享得变量,一个线程通常先获取锁定并去清楚它得内存工作区,把这些共享变量从所有的内存共享区中正确的装入到它自己所在的工作内存区中,当线程解锁时保证该工作内存区中的变量的值写回到共享内存中。
一个线程可以执行的操作有使用(use)、赋值(assign)、装载(load)、存储(store)、锁定(lock)、解锁(unlock)。
而主内存可以执行的操作有读(read)、写(write)、锁定(lock)、解锁(unlock)。每个操作都是原子的。
Volatile的作用就是强制线程到主内存(共享内存)里去读取变量,而不去线程工作内存区中去读取。从而实现了多个线程间的变量可见,也就是线程安全的可见性。
Volatile只支持可见性,不支持原子性。
Volatile不具备原子性 和 原子性操作 示例:
package edu.sdut.thread01;
import java.util.concurrent.atomic.AtomicInteger;
public class VolatileNoAtomic extends Thread {
//private static volatile int count; //使用volatile时不具备原子性 private static AtomicInteger count = new AtomicInteger(0);//具备原子性的类变量 private static void addCount() { for(int i = 0;i<1000;i++) { //count++;//使用volatile时不具备原子性 count.incrementAndGet();//变量++ } System.out.println(count); } public void run() { addCount(); } public static void main(String[] args) { VolatileNoAtomic[] arr = new VolatileNoAtomic[10]; for(int i= 0;i<10;i++) { arr[i] = new VolatileNoAtomic(); } for(int i = 0;i<10;i++) { arr[i].start(); } } }
|
上述示例中使用了具有原子性的API:AtomicInteger ;在包atomic下还有其他的原子变量,如AtomicDouble等。
Volatile总结:volatile关键字只具有可见性,没有原子性。要实现原子性建议使用atomic类的系列对象,支持原子操作。