线程锁定状态下调用 sleep() 方法详解
问题分析
当线程在持有锁的情况下调用 sleep() 方法时,会导致以下问题:
1. 资源浪费和性能问题
-
线程在睡眠期间不会释放持有的锁
-
其他需要该锁的线程会被阻塞,无法执行
-
系统吞吐量下降,响应时间增加
2. 潜在的死锁风险
-
如果多个线程以不同的顺序持有锁并调用 sleep,可能导致死锁
代码示例
问题代码示例
java
public class SleepWithLockExample {
private final Object lock = new Object();
public void problemMethod() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " 获得了锁");
try {
// 问题:在持有锁的情况下睡眠
Thread.sleep(5000); // 睡眠5秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " 释放了锁");
}
}
public static void main(String[] args) {
SleepWithLockExample example = new SleepWithLockExample();
// 创建多个线程竞争锁
for (int i = 0; i < 3; i++) {
new Thread(() -> example.problemMethod(), "Thread-" + i).start();
}
}
}
修复方案
方案1:缩小同步范围
java
public class FixedExample1 {
private final Object lock = new Object();
public void fixedMethod() {
// 只同步必要的代码块
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " 执行关键操作");
// 关键操作代码
}
// 在锁外执行耗时操作
try {
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + " 完成睡眠");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 如果需要再次同步
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " 执行后续关键操作");
}
}
}
方案2:使用等待/通知机制
java
public class FixedExample2 {
private final Object lock = new Object();
private boolean condition = false;
public void waitingMethod() throws InterruptedException {
synchronized (lock) {
while (!condition) {
// 正确方式:调用 wait() 会释放锁
lock.wait(5000); // 最多等待5秒
System.out.println(Thread.currentThread().getName() + " 被唤醒或超时");
}
// 条件满足后的处理
System.out.println(Thread.currentThread().getName() + " 条件满足,继续执行");
}
}
public void notifyingMethod() {
synchronized (lock) {
condition = true;
lock.notifyAll(); // 唤醒所有等待线程
}
}
}
方案3:使用 Lock 接口的 tryLock
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class FixedExample3 {
private final Lock lock = new ReentrantLock();
public void tryLockMethod() {
if (lock.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + " 获得了锁");
// 执行关键操作
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放了锁");
}
} else {
System.out.println(Thread.currentThread().getName() + " 未能获得锁,执行其他操作");
}
// 在锁外执行耗时操作
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
方案4:使用 CountDownLatch 或 CyclicBarrier
java
import java.util.concurrent.CountDownLatch;
public class FixedExample4 {
private final Object lock = new Object();
private final CountDownLatch latch = new CountDownLatch(1);
public void coordinatedMethod() throws InterruptedException {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " 执行关键操作");
// 关键操作
}
// 使用 CountDownLatch 协调线程
System.out.println(Thread.currentThread().getName() + " 等待其他操作完成");
latch.await(); // 不会持有锁
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " 继续执行");
}
}
public void signalCompletion() {
latch.countDown();
}
}
最佳实践总结
-
最小化同步范围:只在必要时持有锁,尽快释放
-
避免在同步块中执行耗时操作:包括 I/O 操作、网络请求、长时间计算等
-
使用合适的并发工具:
-
wait()/notify()用于线程间协调 -
Lock接口提供更灵活的锁机制 -
CountDownLatch、CyclicBarrier用于线程协调 -
Semaphore控制资源访问
-
-
考虑使用无锁编程:
-
使用
ConcurrentHashMap等并发集合 -
使用原子变量类 (
AtomicInteger等) -
使用不可变对象
-
检测工具
可以使用以下工具检测锁相关问题:
-
静态分析工具:SpotBugs、FindBugs
-
动态分析工具:Java Mission Control、VisualVM
-
专业分析工具:jstack、jconsole

583

被折叠的 条评论
为什么被折叠?



