Java多线程——synchronized、volatile 保障可见性
synchronized
关键字和Lock
相关的工具类可以保证原子性、可见性和有序性,volatile
关键字可以保证可见性和有序性,不能保证原子性。
可见性:synchronize、volatile
原子性:synchronize
volatile保证数据的可见性,但是不保证原子性(多线程进行写操作,不保证线程安全);而synchronized是一种排他(互斥)的机制。
主线程无法感知t1线程修改了flag值:java
代码解读复制代码private static Boolean flag = false;
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
});
t1.start();
while (true) {
if (flag) {
System.out.println("线程修改了flag");
break;
}
}
}
解决1:synchronized。 程序执行synchronized后,会清空工作内存副本数据,之后就从新从主内存获取数据kotlin
代码解读复制代码while (true) {
synchronized (flag) {
if (flag) {
System.out.println("线程修改了flag");
break;
}
}
}
某一个线程进入synchronized代码块前后,执行过程入如下:
线程获得锁
清空工作内存
从主内存拷贝共享变量最新的值到工作内存成为副本
执行代码
将修改后的副本的值刷新回主内存中
线程释放锁
解决2:volatile 也可以保证多线程之间访问共享变量时的可见性。java
代码解读复制代码public class App10 {
private static volatile Boolean flag = false;
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
});
t1.start();
while (true) {
if (flag) {
System.out.println("线程修改了flag");
break;
}
}
}
}
子线程t从主内存读取到数据放入其对应的工作内存
将flag的值更改为true,但是这个时候flag的值还没有写会主内存
此时main方法main方法读取到了flag的值为false
当子线程t将flag的值写回去后,失效其他线程对此变量副本
再次对flag进行操作的时候线程会从主内存读取最新的值,放入到工作内存中
总结: volatile保证不同线程对共享变量操作的可见性,也就是说一个线程修改了volatile修饰的变量,当修改写回主内存时,另外一个线程立即看到最新的值。