Java并发编程实战笔记

Java并发编程实战笔记

第一部分:并发编程Bug的根源

1. 硬件层面的矛盾

  • CPU、内存、I/O设备的速度差异
    • CPU vs 内存:一天 vs 一年
    • 内存 vs I/O:一天 vs 十年
  • 系统的应对方案:
    • CPU增加缓存
    • 操作系统增加进程、线程
    • 编译程序优化指令执行顺序

2. 并发编程的三大问题

2.1 可见性问题
  • 定义:一个线程对共享变量的修改,其他线程无法立即看到
  • 根源:CPU缓存导致
  • 示例:多核场景下的计数器问题
class Test {
    private long count = 0;
    void add10K() {
        int idx = 0;
        while(idx++ < 10000) {
            count += 1;
        }
    }
}
2.2 原子性问题
  • 定义:一个操作不能被线程调度机制中断的特性
  • 根源:线程切换
  • CPU指令级别的原子性:
    • 一条普通指令可能需要多条CPU指令完成
    • 线程切换可能发生在任何一条CPU指令后
2.3 有序性问题
  • 定义:程序执行顺序与代码编写顺序不一致
  • 根源:编译器优化
  • 案例:DCL(双重检查锁定)单例模式的隐患

第二部分:Java内存模型的解决方案

1. Java内存模型(JMM)的设计原则

  • 程序员可以自主选择是否禁用缓存和编译优化
  • 需要提供简单易用的禁用方法
  • 兼顾性能和正确性

2. 核心技术方案

2.1 volatile关键字
  • 功能:禁用CPU缓存
  • 1.5版本增强:
    • 不仅保证可见性
    • 还能保证部分有序性
  • 适用场景:
    • 单个共享变量的读写
    • 对变量的写操作不依赖当前值
2.2 Happens-Before规则

六大规则:

  1. 程序顺序规则

    • 单线程内代码前后顺序可见性保证
  2. volatile变量规则

    • 写volatile变量对后续读volatile变量可见
  3. 传递性规则

    • A Happens-Before B
    • B Happens-Before C
    • 则A Happens-Before C
  4. 管程中锁规则(synchronized)

    • 解锁Happens-Before加锁
  5. 线程start()规则

    • start()前对变量的修改对新线程可见
  6. 线程join()规则

    • 子线程的操作对join()之后的主线程可见
2.3 final关键字
  • 作用:告诉编译器这是不可变量
  • 注意事项:
    • 1.5版本后增强了重排序约束
    • 避免构造函数中this引用逸出

3. 实践指南

3.1 选择合适的方案
  • volatile:适用于单个变量的可见性
  • synchronized:适用于复合操作的原子性
  • Happens-Before:理解跨线程的可见性保证
3.2 代码实践建议
  • 优先使用成熟的工具类
  • 避免过度使用volatile
  • 理解happens-before对可见性的保证
  • 合理使用final提升性能

思考题

Q: 如何确保一个线程写入的变量对其他线程可见?
A: 以下几种方式:

  1. 使用volatile修饰变量
  2. 使用synchronized同步
  3. 使用显式锁(Lock)
  4. 利用happens-before规则(如线程start/join)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值