避免在实用程序中等待/通知以暂停/恢复线程

时间:2013-12-11 10:09:28

标签: java multithreading wait notify

我正在实现以下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();

}

2 个答案:

答案 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,释放许可等于暂停线程数并将计数设置为零。

所有可变状态都由内部锁定保护。