8gu-多线程
多线程
线程中断
状态 | 中断响应 | 异常抛出 | 中断标志 |
---|---|---|---|
TIMED_WAITING (sleep) | 立即唤醒 | InterruptedException | 清除 |
WAITING (wait/join) | 立即唤醒 | InterruptedException | 清除 |
WAITING(park) | 立即唤醒 | InterruptedException | 保留 |
BLOCKED (同步阻塞) | 不立即响应 | 无 | 设置为true |
TIMED_WAITING(SLEEPING)状态
```JAVA ``` public class SleepInterruptExample { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { try { System.out.println("开始睡眠..."); Thread.sleep(10000); } catch (InterruptedException e) { System.out.println("睡眠被中断"); System.out.println("中断标志: " + Thread.currentThread().isInterrupted()); // false } }); thread.start(); Thread.sleep(1000); thread.interrupt(); // 中断睡眠线程 } } ```WAITING/JOIN状态
```JAVA public class WaitInterruptExample { private static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { synchronized (lock) { try { System.out.println("开始等待..."); lock.wait(); // 无限等待 } catch (InterruptedException e) { System.out.println("等待被中断"); System.out.println("中断标志: " + Thread.currentThread().isInterrupted()); // false } } }); thread.start(); Thread.sleep(1000); thread.interrupt(); // 中断等待线程 } } ```WAITING(park)状态
public static void main(String[] args) throws InterruptedException
{
Thread t = new Thread(() -> {
System.out.println("子线程开始执行,sleep 10秒...");
System.out.println(Thread.currentThread().isInterrupted());
LockSupport.park();// 循环判断中断标志位,等待结束出来后中断标志一定为true
System.out.println(Thread.currentThread().isInterrupted());
System.out.println("我被中断了!");
});
t.start();//Lemongo
Thread.sleep(1000);//睡一下,保证t处于park()的WAITING状态
System.out.println("子线程状态:"+ t.getState());//WAITING
t.interrupt();//对子线程进行中断
}
BLOCKED状态
public class BlockedInterruptExample { private static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { // 线程1持有锁 Thread thread1 = new Thread(() -> { synchronized (lock) { try { System.out.println("线程1获得锁,睡眠5秒"); Thread.sleep(5000); } catch (InterruptedException e) { System.out.println("线程1被中断"); } } }); // 线程2尝试获取锁,会被阻塞 Thread thread2 = new Thread(() -> { System.out.println("线程2尝试获取锁..."); synchronized (lock) { System.out.println("线程2获得锁"); // 检查是否被中断 if (Thread.currentThread().isInterrupted()) { System.out.println("线程2在阻塞期间被中断了"); } } }); thread1.start(); Thread.sleep(100); // 确保线程1先获得锁 thread2.start(); Thread.sleep(100); // 确保线程2进入阻塞状态 System.out.println("中断线程2..."); thread2.interrupt(); // 中断阻塞的线程2 thread1.join(); thread2.join(); } }
synchronized锁升级机制详解
1. 概述
synchronized锁升级是JVM为了优化同步性能而设计的机制,锁会根据竞争情况从轻量级逐步升级到重量级。这种渐进式的升级策略在不同的竞争场景下都能提供较好的性能表现。
2. 对象头结构
2.1 Java对象头组成
|--------------------------------------------------------------|
| Object Header (64/128 bits) |
|--------------------------------------------------------------|
| Mark Word (32/64 bits) | Klass Word |
|--------------------------------------------------------------|
2.2 Mark Word在不同锁状态下的结构
// 64位JVM下的Mark Word结构
// 无锁状态
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |
// 偏向锁状态
| thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 |
// 轻量级锁状态
| ptr_to_lock_record:62 | lock:2 |
// 重量级锁状态
| ptr_to_heavyweight_monitor:62 | lock:2 |
// GC标记状态
| ForwardingAddress:62 | lock:2 |
2.3 锁状态标识
lock位 | biased_lock位 | 锁状态 |
---|---|---|
01 | 0 | 无锁 |
01 | 1 | 偏向锁 |
00 | - | 轻量级锁 |
10 | - | 重量级锁 |
11 | - | GC标记 |
3. 锁升级流程图
flowchart TD
A[对象创建] --> B{是否启用偏向锁?}
B -->|是| C[匿名偏向状态]
B -->|否| D[无锁状态]
C --> E{第一个线程访问?}
E -->|是| F[偏向锁状态<br/>记录线程ID]
D --> G{有线程竞争?}
G -->|否| H[保持无锁状态]
G -->|是| I[轻量级锁状态<br/>CAS + 自旋]
F --> J{其他线程访问?}
J -->|否| K[保持偏向锁<br/>同一线程重入无开销]
J -->|是| L[撤销偏向锁]
L --> M[轻量级锁状态<br/>CAS + 自旋]
I --> N{自旋成功?}
M --> N
N -->|是| O[获得轻量级锁]
N -->|否| P{达到自旋阈值?}
P -->|否| Q[继续自旋]
Q --> N
P -->|是| R[膨胀为重量级锁]
O --> S{释放锁?}
S -->|否| T[持有轻量级锁]
S -->|是| U[释放轻量级锁]
R --> V[重量级锁状态<br/>操作系统互斥量]
V --> W{获得锁?}
W -->|否| X[线程阻塞等待]
W -->|是| Y[持有重量级锁]
X --> Z[进入等待队列]
Z --> AA[LockSupport.park()]
AA --> AB{被唤醒?}
AB -->|是| W
AB -->|否| AA
Y --> AC{释放锁?}
AC -->|否| Y
AC -->|是| AD[释放重量级锁<br/>唤醒等待线程]
4. 锁升级详细过程
4.1 无锁 → 偏向锁
触发条件:
- JVM启动4秒后(可通过
-XX:BiasedLockingStartupDelay=0
关闭延迟) - 第一个线程访问同步块
升级过程:
- 检查Mark Word的偏向锁标志位
- 如果对象支持偏向锁,使用CAS将当前线程ID写入Mark Word
- 成功后,该线程后续进入同步块无需任何同步操作
public class BiasedLockExample {
private static final Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (lock) {
System.out.println("线程1获得偏向锁");
// Mark Word记录线程1的ID
// 后续该线程重入无需CAS操作
synchronized (lock) {
System.out.println("线程1重入偏向锁");
}
}
});
thread1.start();
}
}
4.2 偏向锁 → 轻量级锁
触发条件:
- 其他线程尝试获取已被偏向的锁
- 偏向锁的线程已经退出同步块
升级过程:
- 暂停持有偏向锁的线程
- 检查该线程是否还在同步块中
- 如果不在,撤销偏向锁,恢复到无锁状态
- 如果还在,升级为轻量级锁
- 恢复线程执行
public class LightweightLockExample {
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
// 线程1获得偏向锁
Thread thread1 = new Thread(() -> {
synchronized (lock) {
System.out.println("线程1获得偏向锁");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
thread1.start();
thread1.join();
// 线程2尝试获取锁,触发偏向锁撤销
Thread thread2 = new Thread(() -> {
synchronized (lock) {
System.out.println("线程2触发锁升级为轻量级锁");
}
});
thread2.start();
}
}
4.3 轻量级锁 → 重量级锁
触发条件:
- 自旋次数达到阈值(默认10次,JDK6后为自适应)
- 自旋线程数超过CPU核心数的一半
- 同时有超过3个线程竞争同一个锁
升级过程:
- 创建ObjectMonitor对象
- 将Mark Word指向ObjectMonitor
- 竞争失败的线程进入阻塞状态
- 使用操作系统的互斥量进行同步
public class HeavyweightLockExample {
private static final Object lock = new Object();
private static volatile boolean ready = false;
public static void main(String[] args) throws InterruptedException {
// 创建多个线程同时竞争锁
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
final int threadId = i;
threads[i] = new Thread(() -> {
while (!ready) {
Thread.yield();
}
synchronized (lock) {
System.out.println("线程" + threadId + "获得重量级锁");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
threads[i].start();
}
Thread.sleep(100);
ready = true; // 触发激烈竞争
for (Thread thread : threads) {
thread.join();
}
}
}
5. 核心实现机制
5.1 偏向锁实现
// 偏向锁获取伪代码
public boolean acquireBiasedLock(Object obj, Thread currentThread) {
MarkWord markWord = obj.getMarkWord();
if (markWord.isBiasedLock()) {
if (markWord.getBiasedThread() == currentThread) {
// 当前线程已持有偏向锁,直接返回
return true;
} else if (markWord.getBiasedThread() == null) {
// 匿名偏向状态,尝试CAS设置偏向线程
return obj.compareAndSwapMarkWord(markWord,
markWord.setBiasedThread(currentThread));
} else {
// 偏向其他线程,需要撤销偏向锁
revokeBiasedLock(obj);
return acquireLightweightLock(obj, currentThread);
}
}
return acquireLightweightLock(obj, currentThread);
}
5.2 轻量级锁实现
// 轻量级锁获取伪代码
public boolean acquireLightweightLock(Object obj, Thread currentThread) {
// 在当前线程栈帧中创建Lock Record
LockRecord lockRecord = currentThread.createLockRecord();
// 将对象头的Mark Word复制到Lock Record的Displaced Mark Word中
lockRecord.setDisplacedMarkWord(obj.getMarkWord());
// 使用CAS尝试将对象头的Mark Word替换为指向Lock Record的指针
if (obj.compareAndSwapMarkWord(obj.getMarkWord(),
createLightweightLockMarkWord(lockRecord))) {
// CAS成功,获得轻量级锁
return true;
} else {
// CAS失败,进入自旋或升级为重量级锁
return handleLightweightLockContention(obj, lockRecord);
}
}
private boolean handleLightweightLockContention(Object obj, LockRecord lockRecord) {
int spinCount = getAdaptiveSpinCount(); // 自适应自旋次数
for (int i = 0; i < spinCount; i++) {
if (obj.compareAndSwapMarkWord(obj.getMarkWord(),
createLightweightLockMarkWord(lockRecord))) {
return true;
}
// 自旋优化:可能包括yield、pause指令等
optimizedSpin();
}
// 自旋失败,升级为重量级锁
return inflateToHeavyweightLock(obj);
}
5.3 重量级锁实现
// ObjectMonitor结构
class ObjectMonitor {
private volatile Thread owner; // 持有锁的线程
private volatile int recursions; // 重入次数
private volatile Thread successor; // 假定的继承线程
private ParkEvent entryList; // 等待获取锁的线程队列
private ParkEvent waitSet; // 调用wait()方法的线程队列
public boolean tryLock(Thread thread) {
if (compareAndSwapOwner(null, thread)) {
recursions = 1;
return true;
}
if (owner == thread) {
recursions++; // 重入
return true;
}
return false;
}
public void lock(Thread thread) {
if (tryLock(thread)) {
return;
}
// 进入等待队列
entryList.park(thread);
// 被唤醒后重新尝试获取锁
while (!tryLock(thread)) {
entryList.park(thread);
}
}
public void unlock(Thread thread) {
if (owner != thread) {
throw new IllegalMonitorStateException();
}
if (--recursions == 0) {
owner = null;
// 唤醒等待队列中的一个线程
entryList.unpark();
}
}
}
6. 性能对比分析
6.1 各种锁的性能特点
锁类型 | 获取/释放开销 | 适用场景 | 优点 | 缺点 |
---|---|---|---|---|
偏向锁 | 几乎无开销 | 单线程访问 | 性能最优 | 撤销开销大 |
轻量级锁 | CAS操作 | 短时间竞争 | 无阻塞 | 自旋消耗CPU |
重量级锁 | 系统调用 | 长时间竞争 | 不消耗CPU | 上下文切换开销大 |
6.2 性能测试代码
public class LockPerformanceTest {
private static final int ITERATIONS = 1000000;
private final Object lock = new Object();
private int counter = 0;
// 测试偏向锁性能(单线程)
public void testBiasedLock() {
long start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
synchronized (lock) {
counter++;
}
}
long end = System.nanoTime();
System.out.println("偏向锁耗时: " + (end - start) / 1000000 + "ms");
}
// 测试轻量级锁性能(轻度竞争)
public void testLightweightLock() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
Thread thread1 = new Thread(() -> {
for (int i = 0; i < ITERATIONS / 2; i++) {
synchronized (lock) {
counter++;
}
}
latch.countDown();
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < ITERATIONS / 2; i++) {
synchronized (lock) {
counter++;
}
}
latch.countDown();
});
long start = System.nanoTime();
thread1.start();
thread2.start();
latch.await();
long end = System.nanoTime();
System.out.println("轻量级锁耗时: " + (end - start) / 1000000 + "ms");
}
// 测试重量级锁性能(激烈竞争)
public void testHeavyweightLock() throws InterruptedException {
int threadCount = 10;
CountDownLatch latch = new CountDownLatch(threadCount);
long start = System.nanoTime();
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
for (int j = 0; j < ITERATIONS / threadCount; j++) {
synchronized (lock) {
counter++;
try {
Thread.sleep(1); // 模拟长时间持有锁
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
latch.countDown();
}).start();
}
latch.await();
long end = System.nanoTime();
System.out.println("重量级锁耗时: " + (end - start) / 1000000 + "ms");
}
}
7. JVM参数调优
7.1 偏向锁相关参数
# 启用/禁用偏向锁(默认启用)
-XX:+UseBiasedLocking
-XX:-UseBiasedLocking
# 设置偏向锁启动延迟时间(默认4000ms)
-XX:BiasedLockingStartupDelay=0
# 设置偏向锁批量重偏向阈值(默认20)
-XX:BiasedLockingBulkRebiasThreshold=20
# 设置偏向锁批量撤销阈值(默认40)
-XX:BiasedLockingBulkRevokeThreshold=40
7.2 自旋锁相关参数
# JDK6及之前版本的自旋参数
-XX:+UseSpinning # 启用自旋锁
-XX:PreBlockSpin=10 # 自旋次数
# JDK6之后使用自适应自旋
-XX:+UseAdaptiveSizePolicy # 启用自适应策略
7.3 调试参数
# 打印偏向锁相关信息
-XX:+TraceBiasedLocking
# 打印类加载信息
-XX:+TraceClassLoading
# 使用JOL(Java Object Layout)工具查看对象布局
# 需要添加依赖:org.openjdk.jol:jol-core
8. 锁优化技术
8.1 锁消除
JIT编译器在运行时如果检测到不可能存在共享数据竞争的锁,会自动消除这些锁。
public class LockElimination {
// 锁消除示例:JIT编译器会消除StringBuffer的内部锁
public String concatenate(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
// sb是局部变量,不会被其他线程访问,synchronized会被消除
}
}
8.2 锁粗化
如果JIT编译器检测到一系列连续的操作都对同一个对象反复加锁和解锁,会将加锁同步的范围扩展到整个操作序列的外部。
public class LockCoarsening {
private final Object lock = new Object();
// 锁粗化前
public void beforeCoarsening() {
synchronized (lock) {
doSomething1();
}
synchronized (lock) {
doSomething2();
}
synchronized (lock) {
doSomething3();
}
}
// 锁粗化后(JIT编译器自动优化)
public void afterCoarsening() {
synchronized (lock) {
doSomething1();
doSomething2();
doSomething3();
}
}
private void doSomething1() {}
private void doSomething2() {}
private void doSomething3() {}
}
9. 实际应用建议
9.1 选择合适的同步机制
// 1. 单线程或主要由单线程访问:使用偏向锁(默认)
public class SingleThreadAccess {
private final Object lock = new Object();
public void singleThreadMethod() {
synchronized (lock) {
// 单线程访问,偏向锁性能最优
}
}
}
// 2. 短时间竞争:轻量级锁表现良好
public class ShortContention {
private final Object lock = new Object();
public void shortCriticalSection() {
synchronized (lock) {
// 短时间持有锁,适合轻量级锁
int result = calculate();
}
}
}
// 3. 长时间竞争:考虑使用其他同步机制
public class LongContention {
private final ReentrantLock lock = new ReentrantLock();
public void longCriticalSection() {
lock.lock();
try {
// 长时间持有锁,ReentrantLock可能更合适
performLongOperation();
} finally {
lock.unlock();
}
}
}
9.2 避免锁升级的最佳实践
public class BestPractices {
// 1. 减少锁的持有时间
public void reduceLockHoldTime() {
// 准备工作在锁外完成
Object data = prepareData();
synchronized (this) {
// 只在必要时持有锁
updateSharedState(data);
}
// 后续处理在锁外完成
postProcess();
}
// 2. 减少锁的粒度
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void fineLockGranularity() {
synchronized (lock1) {
// 操作资源1
}
synchronized (lock2) {
// 操作资源2
}
}
// 3. 使用读写锁分离读写操作
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
public void readOperation() {
rwLock.readLock().lock();
try {
// 读操作
} finally {
rwLock.readLock().unlock();
}
}
public void writeOperation() {
rwLock.writeLock().lock();
try {
// 写操作
} finally {
rwLock.writeLock().unlock();
}
}
}
10. 总结
synchronized锁升级机制是JVM的一项重要优化技术,通过以下几个阶段实现性能优化:
- 偏向锁阶段:适用于单线程访问场景,几乎无同步开销
- 轻量级锁阶段:适用于短时间竞争场景,通过CAS和自旋避免阻塞
- 重量级锁阶段:适用于长时间竞争场景,使用操作系统互斥量
关键要点:
- 渐进式升级:锁只能升级不能降级,确保线程安全
- 自适应优化:JVM根据运行时情况动态调整策略
- 场景适配:不同竞争程度下使用最适合的锁实现
- 性能平衡:在同步开销和竞争处理之间找到最佳平衡点
理解synchronized锁升级机制有助于编写高性能的并发程序,合理选择同步策略,避免不必要的性能损失。