1、volatile
概念
:轻量级锁。
使用场景
:多个线程同时对未加volatile 的变量读写,会导致数据不准确。
使用
:
volatile boolean status = false;
volatile特性
:
-
保证共享变量对所有线程的可见性(只对原子操作有效,针对复合操作无效(比如:num++))。
1)当
写
一个volatile变量时,JMM会把该线程对应的本地内存中的变量强制刷新到主内存中去;2)这个
写
会操作会导致其他线程中的
缓存无效
。 -
禁止指令
重排序
优化
其他概念
:
Java内存模型
(
JMM
):JMM定义了线程和主内存之间的抽象关系:共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存保存了被该线程使用到的主内存的副本拷贝,线程对变量的所有操作都必须在工作内存(副本)中进行,而不能直接读写主内存中的变量。这三者之间的交互关系如下:
可见性
:是指当一条线程修改了共享变量的值,新值对于其他线程来说是可以立即得知的。
重排序
:是指编译器和处理器为了优化程序性能而对指令序列进行排序的一种手段。
原子操作
:
注意
:
(1) 对复合类操作无效
:
num++不是个原子性的操作,而是个复合操作。(读取、加一、赋值)
public class Counter {
public static volatile int num = 0;
//使用CountDownLatch来等待计算线程执行完
static CountDownLatch countDownLatch = new CountDownLatch(30);
public static void main(String []args) throws InterruptedException {
//开启30个线程进行累加操作
for(int i=0;i<30;i++){
new Thread(){
public void run(){
for(int j=0;j<10000;j++){
num++;//自加操作 复和操作
}
countDownLatch.countDown();
}
}.start();
}
//等待计算线程执行完
countDownLatch.await();
System.out.println(num); //执行结果小于300000
}
}
在多线程环境下,有可能线程A将num读取到本地内存中,此时其他线程可能已经将num增大了很多,线程A依然对过期的num进行自加,重新写到主存中,最终导致了num的结果不合预期,而是小于30000。
解决num++操作的原子性问题
:
针对num++这类复合类的操作,可以使用java并发包中的原子操作类解决。
原子操作类
:是通过循环CAS的方式来保证其原子性的。
public class Counter {
//使用原子操作类
public static AtomicInteger num = new AtomicInteger(0);
//使用CountDownLatch来等待计算线程执行完
static CountDownLatch countDownLatch = new CountDownLatch(30);
public static void main(String []args) throws InterruptedException {
//开启30个线程进行累加操作
for(int i=0;i<30;i++){
new Thread(){