[英]I want to stop a group of completed threads, wait for uncompleted threads
我需要一組線程同時運行,然后再運行另一組線程。 例如,10 個線程開始工作,然后是 10 或 15 個其他線程。 當然,我嘗試的第一種方法是制作循環。
while (true) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(
new Runnable() {
@Override
public void run() {
System.out.println("hi");
}
});
thread.start();
}
}
但問題是當這種情況發生時:想象一下,如果在第一次迭代中,8 個線程完成了它們的任務,而 2 個線程需要更長的時間。 在所有 8 + 2(已完成和未完成)線程完成之前,接下來的 10 個線程不會開始。 而我想要一種方法,將 8 個線程替換為 8 個等待啟動線程。
要將任務添加到線程並替換它們,您可以使用ExecutorService 。 您可以使用以下方法創建它:
ExecutorService executor = Executors.newFixedThreadPool(10);
它可以使用裸Thread
和Runnable
來完成,而無需深入研究更先進的技術。
為此,您需要執行以下步驟:
Runnable
接口的實現);join()
(請注意,首先我們需要啟動所有線程)。這就是它的樣子:
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> System.out.println("hi");
int counter = 0;
while (true) {
System.out.println("iteration: " + counter++);
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(task));
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
Thread.currentThread().sleep(1000);
}
}
與其手動管理線程,不如查看ExecutorService
接口的實現提供的設施絕對是明智的。
如果您為任務使用Callable
接口而不是Runnable
,事情會變得更加朴實。 Callable
在許多情況下更方便,因為它允許從工作線程獲取結果,並且如果出現問題也可以傳播異常(相反run()
會強制您捕獲每個已檢查的異常)。 如果您有比打印虛擬消息更有趣的想法,您可能會發現Callable
對您的目的有用。
ExecutorService
有一個阻塞方法invokeAll()
,它需要一個可調用任務的集合,並在所有任務完成后返回一個已完成的Future
對象列表。
要生成重復元素的輕量級集合(因為我們需要觸發一堆相同的任務),我們可以使用實用方法Collections.nCopies()
。
這是一個重復運行虛擬任務的示例代碼:
ExecutorService executor = Executors.newWorkStealingPool();
while (true) {
executor.invokeAll(Collections.nCopies(10, () -> {
System.out.println("hi");
return true;
}));
}
為了確保它符合預期,我們可以添加一個迭代計數器並將其顯示在控制台和Thread.currentThread().sleep()
上,以避免非常快地弄亂 output(出於同樣的原因,任務數量減少了至3
):
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newWorkStealingPool();
int counter = 0;
while (true) {
System.out.println("iteration: " + counter++);
executor.invokeAll(Collections.nCopies(3, () -> {
System.out.println("hi");
return true;
}));
Thread.currentThread().sleep(1000);
}
}
Output:
iteration: 0
hi
hi
hi
iteration: 1
hi
hi
hi
... etc.
另一種可能性是使用CompletableFuture
API,它的方法allOf()
期望以CompletableFuture
形式提交的任務的可變參數並返回單個CompletableFuture
,當所有提供的 arguments 完成時,該方法將完成。
為了使任務的執行與主線程同步,我們需要在生成的CompletableFuture
實例上調用join()
。
這就是它的實現方式:
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newWorkStealingPool();
Runnable task = () -> System.out.println("hi");
int counter = 0;
while (true) {
System.out.println("iteration: " + counter++);
CompletableFuture.allOf(
Stream.generate(() -> task)
.limit(3)
.map(t -> CompletableFuture.runAsync(t, executor))
.toArray(CompletableFuture<?>[]::new)
).join();
Thread.currentThread().sleep(1000);
}
}
Output:
iteration: 0
hi
hi
hi
iteration: 1
hi
hi
hi
... etc.
我懷疑您可能有興趣安排這些任務,而不是據稱運行它們。 如果是這種情況,請查看ScheduledExecutorService
及其方法scheduleAtFixedRate()
和scheduleWithFixedDelay()
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.