Java并发编程实战笔记三(可见性)

博客介绍了多线程环境下的内存可见性问题。在多线程中,读操作可能看不到其他线程写入的值,会产生失效数据。还给出两种解决方式:加锁可确保原子性和可见性;Volatile关键字是轻量级同步机制,只能确保可见性,适用于特定场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1 内存可见性

      意味着当一个线程更新了共享数据时对其他线程可见。

 

1.1.1 可见性

     可见性是一种复杂的属性。在单线程环境中,如果向某个变量写入值,然后在没有其他写入操作的情况下读取这个变量,总能得到相同的值。但是,在多线程环境中,读操作和写操作在不同的线程中执行,情况却并非如此。

代码示例:

	public static void main(String[] args) throws InterruptedException {
		FlagClass fc = new FlagClass();

		new Thread(() -> {
			while (!fc.isFlag()) { // 读取flag的值为true时结束循环

			}
			System.out.println("---->end");
		}).start();

		TimeUnit.MILLISECONDS.sleep(200);
		fc.setFlag(true); // 修改flag的值为true
	}

	private static class FlagClass {

		private boolean flag = false;

		public boolean isFlag() {
			return flag;
		}

		public void setFlag(boolean flag) {
			this.flag = flag;
		}
	}

启动读操作的线程可能会一直循环下去,不会执行打印语句。因为无法保证执行读操作的线程能够实时的看到其他(main)线程写入(更新)的值,因此对于读操作的线程来说是不可见的。

 

1.1.2 失效数据

      在上面的程序中可能产生错误结果的一种情况:失效数据。当读线程查看flag变量时,可能会得到一个已经失效的值(因为flag变量已经被其他线程修改过)。除非在每次访问变量时都使用同步,否则很可能获得该变量的一个失效值,从而导致程序得到错误的值,无法正常结束。

 

1.1.3 可见性的两种解决方式

1. 加锁与可见性

          当线程执行同步代码块获取锁时,重新去主内存读取数据;释放锁之前更新主内存。

简单来说:当一个线程执行由锁保护的同步代码块时,可以看到上一个线程在同一个同步代码块中的所有操作结果。

private static class FlagClass {

	private boolean flag = false;

	public synchronized boolean isFlag() { // 加锁
		return flag;
	}

	public synchronized void setFlag(boolean flag) { // 加锁
		this.flag = flag;
	}
}

加锁(加锁机制)的含义不仅仅局限于互斥行为(原子性),还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步。

2. Volatile关键字

      一种比 synchronized 关键字轻量级的同步机制,用来确保变量的更新操作通知到其他线程。

private static class FlagClass {

	private volatile boolean flag = false; // 变量声明为volatile类型

	public  boolean isFlag() {
		return flag;
	}

	public  void setFlag(boolean flag) {
		this.flag = flag;
	}
}

在访问 volatile 变量时不会执行加锁操作,因此在多线程修改变量时不能确保原子性。

禁止重排序:不会将该变量上的操作与其他内存操作一起重排序。

重排序:编译器、处理器运行时对代码的执行顺序进行优化。

 

总结

      加锁机制可以确保原子性和可见性,而 volatile 变量只能确保可见性。

      如果只是为了多线程之间的通信效果,而不是为了互斥访问,就使用 volatile ,它更加简洁,性能也更好。

 volatile 变量使用场景:

  • 用于标记可变状态和结果
  • 多个线程读变量,单个线程去更新变量(不能保证原子性)
  • 不需要加锁(加锁可以确保可见性)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值