我正在实现以下Java接口,以允许线程暂停和恢复。我有一个使用wait()/ notifyAll()的工作版本,但我想知道是否有更简单的方法(比如在java.util.concurrent中使用一些漂亮的小部件)?
public interface Suspender {
/**
* Go into pause mode: any threads which subsequently call maybePause()
* will block. Calling pause() if already in pause mode has no effect.
*/
void pause();
/**
* Leave pause mode: any threads which call maybePause() will not block,
* and any currently paused threads will be unblocked. Calling resume()
* if not in pause mode has no effect.
*/
void resume();
/**
* If the Suspender is in pause mode, block, and only resume execution
* when the Suspender is resumed. Otherwise, do nothing.
*/
void maybePause();
}
答案 0 :(得分:0)
是的,但是通过架构。
class Suspender {
protected static final ScheduledExecutorService executor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
protected final Runnable task;
protected final long period;
protected final TimeUnit unit;
private Future t;
public Suspender(final Runnable task, final long initialDelay, final long period, final TimeUnit unit) {
this.task = task;
this.period = period;
this.unit = unit;
t = executor.scheduleAtFixedRate(task, initialDelay, period, unit);
}
public boolean pause() {
if (t == null) {
return false;
}
if (t.cancel(true)) {
t = null;
return true;
}
return false;
}
public boolean resume() {
if (t == null) {
t = executor.scheduleAtFixedRate(task, 0, period, unit);
return true;
}
return false;
}
}
所以它的作用是根据您的暂停/恢复呼叫基本安排和取消可运行。
显然不暂停一个线程中间任务,这是一件好事,因为如果你可以在执行期间暂停一个线程,它可能会导致各种问题。这从未发布的锁开始,但也包括半开网络连接,半写文件等。
因此,只要您拥有一个具有单个任务的线程,就不要暂停它。只有当您有一个执行重复任务的线程(因此是ScheduledExecutorService)时,您才可以跳过该任务的进一步调用。然后,您的线程可以通过在代码状态期间查询Thread.interrupted()
标志来在内部决定是否可以在此时暂停,其中可以合理地执行暂停/取消。
答案 1 :(得分:0)
在java.uitl.concurrent.Semaphore
持有1许可证的基础上,可以很容易地建立一个实现。
pause()
获取许可证(阻止直到获得许可证)resume()
再次发布许可证。maybePause()
获取许可并阻止它直到获得它。然后再发布它。要在同一个Suspender上处理多个暂停请求,您可以使用另一个信号量作为更改暂停状态的权限,并在暂停和恢复方法中使用它。
修改强>
好的,一旦我开始写作,我意识到它并不像我想象的那么简单。尽管如此,我确实设法使用(甚至只有一个)Semaphore编写实现:
@ThreadSafe
public class SuspenderImpl implements Suspender {
@GuardedBy("stateLock")
private boolean paused;
@GuardedBy("stateLock")
private int pausedThreads;
private final Semaphore token = new Semaphore(0);
private final Object stateLock = new Object();
@Override
public void pause() {
synchronized (stateLock) {
paused = true;
}
}
@Override
public void resume() {
synchronized (stateLock) {
paused = false;
token.release(pausedThreads);
pausedThreads = 0;
}
}
@Override
public void maybePause() {
synchronized (stateLock) {
if (paused) {
pausedThreads++;
} else {
token.release();
}
}
try {
token.acquire();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
基本理念与我原来的理念有很大不同。在此实现中,maybePause()
方法基于暂停的布尔标志决定是否立即释放许可。如果设置,则会向信号量添加许可证并立即获取。如果未设置,则暂停线程的数量会增加,并且信号量上的获取将被阻止。
pause()
方法只是设置标志。 resume()
方法将标志设置为false,释放许可等于暂停线程数并将计数设置为零。
所有可变状态都由内部锁定保护。