本文共 1373 字,大约阅读时间需要 4 分钟。
Java 内存模型
Java 内存模型(Java Memory Model,JMM)定义了在多线程环境下对共享数据(如成员变量、数组)进行读写时的规则,确保数据的可见性、有序性和原子性。这对于开发并发程序至关重要。
1. 原子性
原子性是指一系列操作必须作为一个整体执行,不能被中断。例如,两个线程对一个初始值为 0 的静态变量进行自增和自减操作,结果可能为正、负或零。这是因为 Java 中没有原子性保证,静态变量的自增和自减操作会被分解为多个 JVM 字节码指令,可能导致数据竞争。
解决方法
使用 synchronized 关键字对共享变量进行同步操作。通过锁对象来保证线程进入和退出同步块的原子性。
2. 可见性
可见性指的是一个线程对另一个线程修改的共享变量的修改是可见的。例如,主线程修改一个运行中的子线程变量的值,子线程可能无法感知到主线程的修改,这会导致死锁。
解决方法
使用 volatile 关键字修饰共享变量。这样可以确保线程对变量的读写操作直接操作主存,避免缓存一致性问题。
3. 有序性
有序性保证了线程之间的操作具有特定的顺序。JVM 会在不影响正确性的情况下,重新排列指令的执行顺序。例如,一个线程修改一个对象标记字段的值,另一个线程读取该字段的值,可能会读取到未正式初始化的值。
解决方法
使用 volatile 修饰变量,禁用指令重排。同时,可以通过 happens-before 规则确保某些操作对其他线程的可见性。
4. CAS 与 原子类
CAS(Compare and Swap)是一种乐观锁机制,适用于竞争不激烈的环境。它通过定期重试,避免数据竞争。Java 提供了 AtomicInteger 和 AtomicBoolean 等原子类,基于 CAS 技术实现线程安全。
CAS 机制
通过 compareAndSwap 方法,线程尝试修改共享变量的值。如果在旧值基础上修改成功,线程继续执行;否则,重试。这需要使用 volatile 修饰共享变量。
乐观锁与悲观锁
- 乐观锁:假设没有其他线程会干扰当前线程的操作,适合竞争不激烈的场景。
- 悲观锁:假设会有其他线程干扰,使用
synchronized关键字进行同步。
5. synchronized 优化
Java HotSpot 虚拟机对 synchronized 锁进行了优化,包括轻量级锁、锁膨胀、偏向锁等。
轻量级锁
在没有竞争的情况下,使用轻量级锁进行加锁和解锁操作。轻量级锁基于 CAS 操作,竞争激烈时会升级为重量级锁。
锁膨胀
在竞争激烈时,轻量级锁会膨胀为重量级锁,确保加锁和解锁的原子性。
偏向锁
Java 6 引入了偏向锁,通过线程 ID 和对象的 hashCode 来优化加锁和解锁操作。只有第一次使用偏向锁时需要加锁,后续可以快速重入。
锁优化总结
- 减少上锁时间:同步代码块应尽量简短。
- 减少锁粒度:将锁拆分为多个锁,例如
ConcurrentHashMap。 - 锁粗化:多次加锁操作可以粗化为一次锁。
- 锁消除:通过逃逸分析,减少不必要的同步操作。
- 读写分离:使用
CopyOnWriteArrayList等结构,减少写锁 contention。
通过以上优化,可以在多线程环境下更高效地管理共享资源,确保程序的正确性和性能。
发表评论
最新留言
关于作者