如何在类方法中抛出指定异常并避免多线程场景下的重复调用问题
Booking类多线程安全实现思路
第一步:先定义所需自定义异常
首先实现三个业务异常类,用于不同场景下的错误抛出,示例(Java版本):
// 预订正在进行时重复调用StartBooking抛出 public class BookingInProgressException extends RuntimeException { public BookingInProgressException() { super("Booking is already in progress"); } } // 预订进行中其他线程调用GetBookingStatus抛出 public class ReadinProgressException extends RuntimeException { public ReadinProgressException() { super("Cannot read booking status from other thread while booking is in progress"); } } // 未启动预订就调用GetBookingStatus抛出 public class NoStartBookingException extends RuntimeException { public NoStartBookingException() { super("Booking has not been started yet"); } }
第二步:核心Booking类实现
核心成员设计
- 内部定义
BookingState枚举,管理全生命周期状态:NOT_STARTED(未启动)、IN_PROGRESS(进行中)、FINISHED(已结束) - 用
AtomicReference<BookingState>存储当前状态,保证多线程下状态修改的原子性和可见性,避免竞态条件 - 额外定义
volatile long类型的bookingThreadId变量,存储正在执行预订流程的线程ID,用于GetBookingStatus的调用方校验
方法实现逻辑
StartBooking()逻辑
- 调用原子引用的
compareAndSet方法,尝试将状态从NOT_STARTED改为IN_PROGRESS - 若CAS操作失败,说明已有线程启动了预订流程,直接抛出
BookingInProgressException - CAS操作成功后,将当前线程ID赋值给
bookingThreadId - 执行实际的预订业务逻辑
- 无论业务逻辑执行成功还是抛出异常,都在
finally块中将状态改回FINISHED,并将bookingThreadId重置为-1
GetBookingStatus()逻辑
- 先读取当前状态,如果状态为
NOT_STARTED,直接抛出NoStartBookingException - 如果状态为
IN_PROGRESS,对比当前调用线程ID和存储的bookingThreadId,两者不一致则抛出ReadinProgressException - 校验通过后返回当前的预订状态即可
Java实现示例
import java.util.concurrent.atomic.AtomicReference; public class Booking { private enum BookingState { NOT_STARTED, IN_PROGRESS, FINISHED } private final AtomicReference<BookingState> state = new AtomicReference<>(BookingState.NOT_STARTED); private volatile long bookingThreadId = -1; public void StartBooking() { // 原子性修改状态,避免多线程竞态 if (!state.compareAndSet(BookingState.NOT_STARTED, BookingState.IN_PROGRESS)) { throw new BookingInProgressException(); } try { bookingThreadId = Thread.currentThread().getId(); // 这里写实际的预订业务逻辑 doBookingLogic(); } finally { // 无论执行结果如何,最终都要重置状态 state.set(BookingState.FINISHED); bookingThreadId = -1; } } public BookingState GetBookingStatus() { BookingState currentState = state.get(); if (currentState == BookingState.NOT_STARTED) { throw new NoStartBookingException(); } if (currentState == BookingState.IN_PROGRESS && Thread.currentThread().getId() != bookingThreadId) { throw new ReadinProgressException(); } return currentState; } private void doBookingLogic() { // 模拟预订业务耗时 try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Booking interrupted", e); } } }
注意事项
- 如果预订逻辑包含异步操作,需要等待所有异步任务执行完成后再修改状态为
FINISHED,避免提前释放状态导致的逻辑错误 - 用CAS无锁操作替代重量级锁,性能更高,也不会出现死锁问题
- 所有状态读写都通过原子引用操作,不要直接操作枚举变量,避免多线程下的内存可见性问题
内容的提问来源于stack exchange,提问作者Chsiom Nwike
相关产品推荐
相关产品推荐

