スレッド プール自体を使用してタスクを削除または終了するには、必要な前提条件があります。
タスクが queue に存在する必要があります。
なぜそう言うのですか?
これは、いわゆる「削除タスク」が ThreadPoolExecutor の remove メソッドを参照しているためです。
public boolean remove(Runnable task) {
boolean removed = workQueue.remove(task);
tryTerminate(); // In case SHUTDOWN and now empty
return removed;
}
ご覧のとおり、これをworkQueueから削除する必要があります
。つまり、タスクが既に実行されている場合、スレッド プールを使用して削除することは理論的に不可能です。。
例えば:
private static void testNormalRemove() {
ThreadPoolExecutor tpe = new ThreadPoolExecutor(1, 3, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3));
Thread task1 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("normal1," + Thread.currentThread().getId());
}
}
});
Thread task2 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
System.out.println("normal2," + Thread.currentThread().getId());
}
}
});
tpe.execute(task1);
tpe.execute(task2);
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tpe.getQueue().size());
System.out.println("remove task2," + tpe.remove(task2));
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
task1 と task2 の 2 つのタスクがあり、作成されたスレッド プールのコア スレッドが 1 である場合、task1 は追加された直後に実行され、task2 はキューに追加されて実行を待機します。スレッドプールの原則、その後、タスク2は正常に削除されましたが、タスク1はキューにまったくないため削除できません.
もちろん、いくつかの特別なスレッド プールがあり、各タスクが実際にキューに追加されます。たとえば、ScheduledThreadPoolExecutor がタスクを追加すると、各タスクが実際にキューに追加されます。
public void execute(Runnable command) {
schedule(command, 0, NANOSECONDS);
}
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
private void delayedExecute(RunnableScheduledFuture<?> task) {
if (isShutdown())
reject(task);
else {
super.getQueue().add(task);
if (isShutdown() &&
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else
ensurePrestart();
}
}
この方法で追加されたタスクは削除できます.
もちろん, 追加されたタスクはRunnableScheduledFutureにカプセル化されていることに注意してください. 削除するときは, 削除する前に RunnableScheduledFuture に変換することを忘れないでください. RunnableScheduledFuture を直接、戻り値を削除できます。
他のスレッド プールはそれほど「ラッキー」ではありません. 一般的な規則に従って, それらは corePoolSize を超えた場合にのみキューに参加します. スレッド プール内のスレッドを停止する方法があります. しかし, スレッド プール内のスケジューリング方法のほとんど
はプライベートであり、次のように呼び出すことはできません。
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
//略
}
すでに実行中のスレッドまたはタスクは Worker としてカプセル化されますが、Worker のメソッドは外部に公開されません。そのため、ほとんどのスレッド プールはスレッドを細かく制御できません。もちろん、スレッド自体は非常にきめ細かく制御されます。
一般的に、一度スレッドを起動すると、従来の方法では停止することが難しく、例外をスローしたり、リターンを追加したり、割り込みを強制的に停止させたりする必要がある場合が多い; これらのシーンも良い選択です
.