第一章 volatile关键字概览
1.1 多线程下变量的不可见性
1.1.1 概述
在多线程并发执行下,多个线程修改共享的成员变量,会出现一个线程修改了共享变量的值后,另一个线程不能直接看到该线程修改后的变量的最新值
1.1.2 案例演示
/*
目标:研究一下多线程下变量访问的不可见性现象
准备内容:
1.准备2个线程
2.定义一个成员变量
3.开启两个线程,其中一个线程负责修改,一个线程负责读取
*/
public class VisibilityDemo01 {
// main方法,作为一个主线程
public static void main(String[] args) throws InterruptedException {
myThread myt = new myThread();
Thread t = new Thread(myt);
t.start();
while(true){
if(myt.isFlag()) {
System.out.println("主线程");
}
}
}
}
class myThread implements Runnable{
//成员变量
private boolean flag = false;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println(true);
}
}
19.多线程中卖电影票、送牛奶均为案例演示
1.1.5 结果展示
1.1.4 小结
多线程下修改共享变量会出现变量修改值后的不可见性
1.2 变量不可见性内存语义
1.2.1 概述
1.3 变量不可见性解决方案
1.3.2 结局方案
加锁
某一个线程进入synchronized代码块前后,执行过程如下:
a.线程获得锁
b.清空工作内存
c.从主内存拷贝共享变量最新的值到工作内存成为副本
d.执行代码
e.将修改后的副本的值刷新回主内存中
f.线程释放锁
volatile关键字修饰
a.子线程从主内存读取到数据放入其对应的工作内存
b.将flag的值更改为true,但是这个时候flag的值还没有写回主内存
c.此时main方法读取到了flag的值为flase
d.当子线程将flag的值写回去后,其他所有线程对此变量的副本都失效
e.再次对flag进行操作的时候,操作的线程就会从主内存读取最新的值,放入到工作内存中
总结 volatile保证不同线程对共享变量操作的可见性,也就是说一个线程修改了volatile修饰的变量,当修改写回主内存时,另外一个线程立即看到最新的值
第二章 volatile的其他特性
2.1 volatile特性概述
volatile总体概览
在上一章中,我们已经研究完了volatile可以实现并发下共享变量的可见性,除了volatile可以保证可见性外,volatile还具备如下一些突出的特性:
volatile的原子性问题:volatile不能保证原子性操作。
禁止指令重排序:volatile可以防止指令重排序操作。
2.2 volatile不保证原子性
所谓原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行。volatile不保证原子性
2.2.1代码测试:
public class VolatileDemo {
public static void main(String[] args) {
VolatileAtomicThread vat = new VolatileAtomicThread();
for(int x=0;x<100;x++){
new Thread(vat).start();
}
System.out.println(vat.getI());
}
}
class VolatileAtomicThread implements Runnable{
private volatile int i=0;
public int getI() {
return i;
}
@Override
public void run() {
for(int j=0;j<10000;j++){
i++;
System.out.println("count" + i);
}
}
}
2.2.2 小结
在多线程环境下,volatile关键字可以保证共享数据的可见性,但是并不能保证对数据操作的原子性(在多线程环境下volatile修饰的变量也是线程不安全的)。
在多线程环境下,要保证数据的安全性,我们还需要使用锁机制
经自己尝试,synchronized、lock都能保证原子性
2.2.3 问题解决
使用锁机制
synchronized、lock
原子类
概述:java从JDK1.5开始提供了java.util.concurrent。atomic包(简称Atomic包),这个包中的原子操作类提供一种用法简单,性能高效,线程安全地更新一个变量地方式。
AromicInteger
原子型Integer,可以实现原子更新操作
class VolatileAtomicThread01 implements Runnable{
private AtomicInteger ai = new AtomicInteger(); //初始值默认为0
@Override
public void run() {
for(int j=0;j<10000;j++){
System.out.println(Thread.currentThread().getName() + "count" + ai.incrementAndGet());
}
}
}
2.3 禁止指令重排序
2.3.1 概述
什么是重排序