如何正确实现:其他线程访问同步块时抛出MyException?
解决方案
核心问题分析
现有实现的关键缺陷是锁状态检查与锁获取不是原子操作,两个线程可能同时检测到锁未被占用,进而先后进入同步块,导致并行执行的风险。
正确实现方案
结合你的两个需求(禁止并行执行、1分钟内重复调用抛出异常),可以通过ReentrantLock的tryLock()原子操作+原子时间记录来实现:
import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; import static java.util.Objects.requireNonNull; public class YourClass { private final ReentrantLock lock = new ReentrantLock(); private final AtomicLong lastExecutionEndTime = new AtomicLong(0); // 定义1分钟的时间间隔(毫秒) private static final long MIN_EXECUTION_INTERVAL = TimeUnit.MINUTES.toMillis(1); void verySlowMethod(String thisOperationId) { requireNonNull(thisOperationId); long currentTime = System.currentTimeMillis(); // 检查距离上次执行结束是否不足1分钟 if (currentTime - lastExecutionEndTime.get() < MIN_EXECUTION_INTERVAL) { throw new MyException(); } // 原子性尝试获取锁,立即返回结果,不阻塞 if (!lock.tryLock()) { throw new MyException(); } try { // 执行你的业务操作代码 // ... } finally { // 无论业务是否异常,都要释放锁并更新执行结束时间 lastExecutionEndTime.set(System.currentTimeMillis()); lock.unlock(); } } }
方案说明
- 原子锁获取:
tryLock()方法会原子性地尝试获取锁,成功则返回true,失败直接返回false,彻底避免了"检查-加锁"的竞态条件。 - 时间间隔控制:使用
AtomicLong记录上次执行结束时间,保证多线程环境下时间读取与更新的原子性,防止时间判断出现竞态。 - 锁安全释放:将锁的释放和时间更新放在
finally块中,确保无论业务代码是否抛出异常,锁都能被正确释放,避免死锁风险。
简化版(仅处理并行执行)
如果不需要1分钟间隔的限制,仅保留并行控制逻辑即可:
private final ReentrantLock lock = new ReentrantLock(); void verySlowMethod(String thisOperationId) { requireNonNull(thisOperationId); if (!lock.tryLock()) { throw new MyException(); } try { // 业务操作代码 // ... } finally { lock.unlock(); } }
内容的提问来源于stack exchange,提问作者Krzysztof
相关产品推荐
相关产品推荐

