Javaでexecutorserviceに一連のジョブを送信しましたが、どういうわけかこれらのすべてのジョブを一時的に一時停止したいのです。これを行うための最良の方法は何ですか?どのように再開できますか?またはこれを完全に間違っていますか?達成したいこと(つまり、実行サービスを一時停止/再開する機能)については、他のパターンに従う必要がありますか?
私自身の質問に答えるために、PausableThreadPoolExecutor
itself のjavadocsにThreadPoolExecutor
の例を見つけました。これがGuavaのモニターを使用した私のバージョンです:
import com.google.common.util.concurrent.Monitor;
import Java.util.concurrent.ScheduledThreadPoolExecutor;
import Java.util.concurrent.ThreadFactory;
public class PausableExecutor extends ScheduledThreadPoolExecutor {
private boolean isPaused;
private final Monitor monitor = new Monitor();
private final Monitor.Guard paused = new Monitor.Guard(monitor) {
@Override
public boolean isSatisfied() {
return isPaused;
}
};
private final Monitor.Guard notPaused = new Monitor.Guard(monitor) {
@Override
public boolean isSatisfied() {
return !isPaused;
}
};
public PausableExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, threadFactory);
}
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
monitor.enterWhenUninterruptibly(notPaused);
try {
monitor.waitForUninterruptibly(notPaused);
} finally {
monitor.leave();
}
}
public void pause() {
monitor.enterIf(notPaused);
try {
isPaused = true;
} finally {
monitor.leave();
}
}
public void resume() {
monitor.enterIf(paused);
try {
isPaused = false;
} finally {
monitor.leave();
}
}
}
私はあなたの受け入れられた答えについていくつかの批判をしましたが、それらはあまり建設的ではありませんでした...だから、これが私の解決策です。このようなクラスを使用して、一時停止機能が必要なときはいつでもどこでもcheckIn
を呼び出します。 GitHub で見つけてください!
import Java.util.Date;
import Java.util.concurrent.TimeUnit;
import Java.util.concurrent.locks.Condition;
import Java.util.concurrent.locks.ReentrantLock;
/**
* Provides a mechanism to pause multiple threads.
* If wish your thread to participate, then it must regularly check in with an instance of this object.
*
* @author Corin Lawson <[email protected]>
*/
public class Continue {
private boolean isPaused;
private ReentrantLock pauseLock = new ReentrantLock();
private Condition unpaused = pauseLock.newCondition();
public void checkIn() throws InterruptedException {
if (isPaused) {
pauseLock.lock();
try {
while (isPaused)
unpaused.await();
} finally {
pauseLock.unlock();
}
}
}
public void checkInUntil(Date deadline) throws InterruptedException {
if (isPaused) {
pauseLock.lock();
try {
while (isPaused)
unpaused.awaitUntil(deadline);
} finally {
pauseLock.unlock();
}
}
}
public void checkIn(long nanosTimeout) throws InterruptedException {
if (isPaused) {
pauseLock.lock();
try {
while (isPaused)
unpaused.awaitNanos(nanosTimeout);
} finally {
pauseLock.unlock();
}
}
}
public void checkIn(long time, TimeUnit unit) throws InterruptedException {
if (isPaused) {
pauseLock.lock();
try {
while (isPaused)
unpaused.await(time, unit);
} finally {
pauseLock.unlock();
}
}
}
public void checkInUninterruptibly() {
if (isPaused) {
pauseLock.lock();
try {
while (isPaused)
unpaused.awaitUninterruptibly();
} finally {
pauseLock.unlock();
}
}
}
public boolean isPaused() {
return isPaused;
}
public void pause() {
pauseLock.lock();
try {
isPaused = true;
} finally {
pauseLock.unlock();
}
}
public void resume() {
pauseLock.lock();
try {
if (isPaused) {
isPaused = false;
unpaused.signalAll();
}
} finally {
pauseLock.unlock();
}
}
}
例えば:
import Java.util.concurrent.ScheduledThreadPoolExecutor;
import Java.util.concurrent.ThreadFactory;
public class PausableExecutor extends ScheduledThreadPoolExecutor {
private Continue cont;
public PausableExecutor(int corePoolSize, ThreadFactory threadFactory, Continue c) {
super(corePoolSize, threadFactory);
cont = c;
}
protected void beforeExecute(Thread t, Runnable r) {
cont.checkIn();
super.beforeExecute(t, r);
}
}
これには、Continue
のpause
への1回の呼び出しで多くのスレッドを一時停止できるという追加の利点があります。
エグゼキュータで一時停止/再開機能を探していましたが、現在処理中のタスクを待機する追加機能があります。以下は、このSOにawait関数を追加したものです。他の優れた実装のバリエーションです。シングルスレッドのエグゼキューターでテストしていました。基本的な使い方は次のとおりです。
executor.pause();
executor.await(10000); // blocks till current tasks processing ends
クラスコード:
import Java.util.concurrent.ScheduledThreadPoolExecutor;
import Java.util.concurrent.locks.Condition;
import Java.util.concurrent.locks.ReentrantLock;
public class PausableScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
public boolean isPaused;
private ReentrantLock pauseLock = new ReentrantLock();
private Condition unpaused = pauseLock.newCondition();
private Latch activeTasksLatch = new Latch();
private class Latch {
private final Object synchObj = new Object();
private int count;
public boolean awaitZero(long waitMS) throws InterruptedException {
long startTime = System.currentTimeMillis();
synchronized (synchObj) {
while (count > 0) {
if ( waitMS != 0) {
synchObj.wait(waitMS);
long curTime = System.currentTimeMillis();
if ( (curTime - startTime) > waitMS ) {
return count <= 0;
}
}
else
synchObj.wait();
}
return count <= 0;
}
}
public void countDown() {
synchronized (synchObj) {
if (--count <= 0) {
// assert count >= 0;
synchObj.notifyAll();
}
}
}
public void countUp() {
synchronized (synchObj) {
count++;
}
}
}
/**
* Default constructor for a simple fixed threadpool
*/
public PausableScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize);
}
/**
* Executed before a task is assigned to a thread.
*/
@Override
protected void beforeExecute(Thread t, Runnable r) {
pauseLock.lock();
try {
while (isPaused)
unpaused.await();
} catch (InterruptedException ie) {
t.interrupt();
} finally {
pauseLock.unlock();
}
activeTasksLatch.countUp();
super.beforeExecute(t, r);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
try {
super.afterExecute(r, t);
}
finally {
activeTasksLatch.countDown();
}
}
/**
* Pause the threadpool. Running tasks will continue running, but new tasks
* will not start untill the threadpool is resumed.
*/
public void pause() {
pauseLock.lock();
try {
isPaused = true;
} finally {
pauseLock.unlock();
}
}
/**
* Wait for all active tasks to end.
*/
public boolean await(long timeoutMS) {
// assert isPaused;
try {
return activeTasksLatch.awaitZero(timeoutMS);
} catch (InterruptedException e) {
// log e, or rethrow maybe
}
return false;
}
/**
* Resume the threadpool.
*/
public void resume() {
pauseLock.lock();
try {
isPaused = false;
unpaused.signalAll();
} finally {
pauseLock.unlock();
}
}
}
問題は、Runnable/Callable自体が一時停止/再開するタイミングを確認する必要があることです。それはそこで言われています、そしてこれをする多くの方法、そしてそれはこれをするための最良の方法に関するあなたの要件に依存します。ソリューションが何であれ、待機を中断可能にして、スレッドをクリーンにシャットダウンできるようにする必要があります。
私はこれが古いことを知っていますが、これらの答えをすべて試しましたが、一時停止可能なタイマーでやろうとしていたことに対して、どれもうまくいきませんでした。それらはすべて、再開するとすぐに(すべて一度に)スケジュールに従って実行していたすべてのデータを破棄します。
代わりに、このTimer
クラスをGitHub * here で見つけました。これは私にとって本当にうまくいきました。
*このコードは記述せず、見つけただけです。