簡體   English   中英

我想停止一組已完成的線程,等待未完成的線程

[英]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);

裸線程

它可以使用裸ThreadRunnable來完成,而無需深入研究更先進的技術。

為此,您需要執行以下步驟:

  • 定義你的任務(提供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() + 可調用

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.allOf().join() + Runnable

另一種可能性是使用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

我懷疑您可能有興趣安排這些任務,而不是據稱運行它們。 如果是這種情況,請查看ScheduledExecutorService及其方法scheduleAtFixedRate()scheduleWithFixedDelay()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM