ReentrantReadWriteLock使用问题求助:线程执行文件擦除时挂起
搞定ReentrantReadWriteLock导致的线程挂起问题
嘿,结合你的场景——定时读操作和用户触发的文件擦除写操作共用同一个ReentrantReadWriteLock,线程出现挂起,我来帮你梳理下问题根源和解决办法!
先揪出可能的问题根源
- 读锁没释放干净:如果定时读操作没把解锁逻辑放在
finally块里,一旦读过程中抛出异常,读锁会一直被持有,写线程永远拿不到写锁,直接挂死。 - 写锁持有期间做耗时操作:文件擦除本身可能慢,如果拿着写锁干这个,所有后续读请求都会被阻塞,看起来就像线程挂起了。
- 锁升级死锁:如果同一个线程先拿了读锁,又想直接拿写锁,ReentrantReadWriteLock可不支持这种“升级”操作——写锁要求所有读锁都释放,但当前线程还拿着读锁,直接就死锁了。
给你修正后的代码示例
我把你提供的代码片段补全并修复了关键问题,你可以参考:
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class FileDB { private static final String ORDERS_FILENAME = "orders.tsv"; private final ReadWriteLock ordersLock = new ReentrantReadWriteLock(); // 拆分读写锁,用起来更顺手 private final Lock readLock = ordersLock.readLock(); private final Lock writeLock = ordersLock.writeLock(); // 定时读取操作 public List<String> readOrders() { readLock.lock(); try { List<String> orders = new ArrayList<>(); // 实际的文件读取逻辑 try (BufferedReader br = new BufferedReader(new FileReader(ORDERS_FILENAME))) { String line; while ((line = br.readLine()) != null) { orders.add(line); } } catch (IOException e) { // 别吞异常,至少打个日志方便排查 e.printStackTrace(); } return orders; } finally { // 必须放finally!不管有没有异常都要释放读锁 readLock.unlock(); } } // 用户触发的文件擦除操作 public void eraseOrders() { writeLock.lock(); try { // 执行文件擦除,尽量别在锁里做太耗时的操作 Files.deleteIfExists(Paths.get(ORDERS_FILENAME)); // 可选:擦除后创建空文件 Files.createFile(Paths.get(ORDERS_FILENAME)); } catch (IOException e) { e.printStackTrace(); } finally { // 写锁也要在finally里释放 writeLock.unlock(); } } }
关键注意事项要记牢
- 锁的释放必须放finally:这是避免锁泄漏的核心,不管读写操作成功还是抛异常,都得确保锁被释放,不然其他线程永远拿不到锁。
- 别搞锁升级:如果需要先读后写,一定要先释放读锁,再去拿写锁,千万别想着“先读再直接转写”,这会直接导致死锁。
- 写锁里别做慢操作:文件IO、网络请求这类慢活,能移到锁外就移出去,不然所有读请求都会被堵着,体验极差。
- 公平锁可选:如果写操作总是被大量读请求抢占,导致写线程饥饿,可以创建锁的时候传
true用公平锁:new ReentrantReadWriteLock(true),这样锁会按请求顺序分配,避免写线程一直拿不到锁。
排查挂起的小技巧
要是还是出现挂起,用JDK自带的工具就能排查:
- 用
jstack <你的进程ID>命令导出线程堆栈,看看哪些线程在等锁,以及哪个线程拿着锁不放。 - 检查持锁线程是不是卡在某个耗时操作上,或者有没有漏释放锁的情况。
内容的提问来源于stack exchange,提问作者chill appreciator
相关产品推荐
相关产品推荐

