考虑以下(简化)类,旨在允许我的整个组件在完全停止之前进入某个临时状态。
(临时状态的目的是允许组件完成其现有任务,但拒绝任何新任务)。
可以从任意数量的线程多次启动和停止组件。
class StopHandler {
boolean isStarted = false;
synchronized void start() {isStarted = true;}
//synchronized as I do want the client code to block until the component is stopped.
//I might add some async method as well, but let's concentrate on the sync version only.
synchronized void stop(boolean isUrgent) {
if (isStarted) {
if (!isUrgent) {
setGlobalState(PREPARING_TO_STOP); //assume it is implemented
try {Thread.sleep(10_000L);} catch (InterruptedException ignored) {}
}
isStarted = false;
}
}
当前实现的问题是,如果某个客户端代码需要在处于临时状态时紧急停止该组件,它仍然需要等待。
例如:
//one thread
stopHandler.stop(false); //not urgent => it is sleeping
//another thread, after 1 millisecond:
stopHandler.stop(true); //it's urgent, "please stop now", but it will wait for 10 seconds
你会如何实现它?
我可能需要中断睡眠线程,但我没有睡眠线程对象可以调用' interrupt()'。
答案 0 :(得分:1)
如何在调用sleep之前直接在StopHandler字段中存储对当前Thread的引用(由Thread.currentThread()返回)?这将允许您在后续紧急呼叫中中断它,以防线程仍处于活动状态。
答案 1 :(得分:0)
无法找到比Lars建议的解决方案更好的解决方案。 只需要封装睡眠管理以保证完整性。
class SleepHandler {
private final ReentrantLock sleepingThreadLock;
private volatile Thread sleepingThread;
SleepHandler() {
sleepingThreadLock = new ReentrantLock();
}
void sleep(long millis) throws InterruptedException {
setSleepingThread(Thread.currentThread());
Thread.sleep(millis);
setSleepingThread(null);
}
void interruptIfSleeping() {
doWithinSleepingThreadLock(() -> {
if (sleepingThread != null) {
sleepingThread.interrupt();
}
});
}
private void setSleepingThread(@Nullable Thread sleepingThread) {
doWithinSleepingThreadLock(() -> this.sleepingThread = sleepingThread);
}
private void doWithinSleepingThreadLock(Runnable runnable) {
sleepingThreadLock.lock();
try {
runnable.run();
} finally {
sleepingThreadLock.unlock();
}
}
}
使用这个助手类,处理原始问题是微不足道的:
void stop(boolean isUrgent) throws InterruptedException {
if (isUrgent) {sleepHandler.interruptIfSleeping();} //harmless if not sleeping
try {
doStop(isUrgent); //all the stuff in the original 'stop(...)' method
} catch (InteruptedException ignored) {
} finally {
Thread.interrupted(); //just in case, clearing the 'interrupt' flag as no need to propagate it futher
}