Java并发编程实践笔记之—可见性(Visibility)

本文探讨Java并发编程中同步的重要性,包括原子性与内存可见性。通过实例解释了失效数据问题,指出非原子的64位操作的风险,并详细阐述了volatile变量的作用,强调其提供可见性而非原子性。文章建议谨慎使用volatile,推荐在确保可见性的场景下使用,如检查状态标志以决定退出循环等。

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

同步的重要性有两个方面:
  • 实现原子性:防止某个线程正在使用对象状态而另一个线程同时在修改改状态
  • 内存可见性:确保一个线程修改了对象状态后,其他线程能够看到发生的状态变化
失效数据
  • 缺乏同步的程序可能会产生的一种错误情况就是——失效数据
  • 失效数据举例
    //在没有同步的情况下共享数据
    public class NoVisibility {
        private static boolean ready;
        private static int number;
    
        private static class ReaderThread extends Thread {
            public void run() {
                while (!ready)
                   Thread.yield();
                System.out.println(number);
            }
        }
    
        public static void main(String[] args) {
            new ReaderThread().start();
            number = 42;
            ready = true;
       }
    }

引发的问题有

  1. NoVisibility 可能会持续循环下去,因为ReaderThread可能永远看不到ready写入的值
  2. NoVisibility 可能会输出0,因为ReaderThread可能看到了ready写入的值,但是没有看到写入的number的值(这种现象叫重排序)
非原子的64位操作
  • 非volatile类型的64位数值变量(double和long)在多线程程序中使用也是不安全的
  • 如果对该变量的读操作和写操作在不同的线程中执行,那么可能会读取某个值的高32位和另一个值的低32位
  • 用关键字volatile来声明,或者用锁保护
加锁与可见性
  • 为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步
volatile变量
  • 稍弱的同步机制
  • 编译器和runtime都会注意到这个变量是共享的,所以不会将该变量重排序,不会被缓存在寄存器或者其他处理器不可见的地方
  • 行为类似于以下程序清单,但是使用volatile时不会执行加锁操作,因此也不会阻塞线程,比synchronized更轻量级
    @ThreadSafe
     public class SynchronizedInteger {
        @GuardedBy("this") private int value;
    
        public synchronized int get() { return value; }
        public synchronized void set(int value) { this.value = value; }
     }

  • 仅当volatile变量能简化代码的实现以及对同步策略的验证时,才应该使用它们,不建议过度依赖volatile变量提供的可见性
  • 通常用作某个操作完成、发生中断或者状态的标志
  • 典型用法:检查某个状态标记以判断是否退出循环。以下示例中asleep必须为volatile变量,否则当asleep被另一个线程修改时,执行判断的线程却发现不了。
  • 加锁机制可以确保可见性和原子性,而volatile变量只能确保可见性
    //数绵羊
    volatile boolean asleep;
     ...
        while (!asleep)
            countSomeSheep();

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值