You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在类方法中抛出指定异常并避免多线程场景下的重复调用问题

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()逻辑

  1. 调用原子引用的compareAndSet方法,尝试将状态从NOT_STARTED改为IN_PROGRESS
  2. 若CAS操作失败,说明已有线程启动了预订流程,直接抛出BookingInProgressException
  3. CAS操作成功后,将当前线程ID赋值给bookingThreadId
  4. 执行实际的预订业务逻辑
  5. 无论业务逻辑执行成功还是抛出异常,都在finally块中将状态改回FINISHED,并将bookingThreadId重置为-1

GetBookingStatus()逻辑

  1. 先读取当前状态,如果状态为NOT_STARTED,直接抛出NoStartBookingException
  2. 如果状态为IN_PROGRESS,对比当前调用线程ID和存储的bookingThreadId,两者不一致则抛出ReadinProgressException
  3. 校验通过后返回当前的预订状态即可

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 15:18:05