Java多线程——synchronized、volatile 保障可见性

代码纪元 后端 2024-12-23

引出

Java多线程——synchronized、volatile 保障可见性


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修饰的变量,当修改写回主内存时,另外一个线程立即看到最新的值。

转载来源:https://juejin.cn/post/7414730597828755471

Apipost 私有化火热进行中

评论