首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >java多线程的内存可见性问题,volatile是干什么的?

java多线程的内存可见性问题,volatile是干什么的?

作者头像
用户11935701
发布2025-12-16 08:46:18
发布2025-12-16 08:46:18
1220
举报

引入

首先我们用一段代码引出我们的问题,什么是内存可见性问题?

代码语言:javascript
复制
static class Counter {
 public int flag = 0;
}
public static void main(String[] args) {
	 Counter counter = new Counter();
	 Thread t1 = new Thread(() -> {
		 while (counter.flag == 0) {
		 
		 }
		 System.out.println("循环结束!");
	 });
	 Thread t2 = new Thread(() -> {
		 Scanner scanner = new Scanner(System.in);
		 System.out.println("输⼊⼀个整数:");
		 counter.flag = scanner.nextInt();
	 });
	 t1.start();
	 t2.start();
}

在这段代码中我们想让我们输入的值不是0的时候t1线程结束,那我们试试结果呢?

很明显当我们输入的时候循环并没有暂停,这时就说明我们的程序出现Bug了,而这就是我们要讲的内存可见性问题。

产生原因

内存可见性问题是由编译器优化导致的,一个线程读取,一个线程修改,修改的值并没有被读取线程读到。

编译器,虽然声称优化操作,是能够保证逻辑不变,尤其是在多线程的程序中,编译器的判断可能出现失误可能导致编译器的优化,使优化后的逻辑,和优化前的逻辑出现细节上的偏差。

jvm不知道什么时候才会进行修改,而且它的值始终没变所以将读取内存优化成了读取寄存器,因为寄存器比内存快,而这就导致了它无法识别到内存的修改,导致了误判。所以说t1的读操作不是真正的读内存。

解决方案

sleep

通过sleep()可以让循环时间延长,让其不再优化load()操作(因为相对于整个循环优化的时间无足轻重)。

但是sleep()方法代价太大了,因此引用了volatile(易变的)关键字,既然你编译器无法判别要不要优化,我就手动判别,当它修饰某个关键字的时候,不会把它优化成读寄存器。

volatile

volatile 关键字的作用主要有如下两个:

  1. 保证内存可见性 :基于屏障指令实现,即当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。
  2. 保证有序性:禁止指令重排序。编译时 JVM 编译器遵循内存屏障的约束,运行时靠屏障指令组织指令顺序。

指令重排序:也是编译器优化的一种形式,调整代码运行的先后顺序,以得到提高性能的效果。指令重排序的大前提是逻辑不变,在多线程的环境下,这里的判定可能出现失误。

votaile是用来修饰变量的,当它修饰的时候jvm就不会对这个变量进行读寄存器的优化,而我们之前的问题就可以迎刃而解了~

代码语言:javascript
复制
 public volatile int flag = 0;

感谢各位的观看~如果对你有帮助的话留个关注再走吧

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引入
  • 产生原因
  • 解决方案
    • sleep
    • volatile
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档