簡體   English   中英

至少一個完成后如何啟動線程

[英]How to start thread when at least one is finished

我有 4 個線程。 每 x 秒打印給定字母 x 次。 任務是立即啟動 3 個線程,並在至少一個先前線程完成時啟動第 4 個線程。 我不知道如何通知最后一個線程在適當的時候運行。

CompletableFutures是以一種富有表現力的方式實現這一目標的現代方式。

public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(3);
    
    CompletableFuture<Void> a = CompletableFuture.runAsync(() -> print("A"), executor);
    CompletableFuture<Void> b = CompletableFuture.runAsync(() -> print("B"), executor);
    CompletableFuture<Void> c = CompletableFuture.runAsync(() -> print("C"), executor);
    CompletableFuture.anyOf(a, b, c).thenRunAsync(() -> print("D"), executor);
}

private static void print(String taskName) {
    for (int i = 0; i < 10_000; ++i) {
        System.out.println("Task " + taskName + ": " + i);
    }
}

運行 A、B 和 C,然后當其中任何一個完成后,運行 D。

您可以通過刪除執行程序並僅使用 fork/join 池來進一步簡化它,但它在獨立示例中效果不佳,因為它們是守護線程; 該程序將在執行任何操作之前很快結束。

取決於你的任務是什么? 你關心工作嗎? 線程?

您總是可以通過創建具有三個線程的線程池並為其分配四個任務來“破解它”:

ExecutorService es = Executors.newFixedThreadPool(3);
    es.execute(() -> {
        for (int i = 0; i < 10; i++) {
            System.out.println("A");
            try {Thread.sleep(100);} catch (InterruptedException e) {}
        }
    });
    es.execute(() -> {
        for (int i = 0; i < 10; i++) {
            System.out.println("B");
            try {Thread.sleep(100);} catch (InterruptedException e) {}
        }
    });
    es.execute(() -> {
        for (int i = 0; i < 10; i++) {
            System.out.println("C");
            try {Thread.sleep(100);} catch (InterruptedException e) {}
        }
    });
    es.execute(() -> {
        for (int i = 0; i < 10; i++) {
            System.out.println("D");
            try {Thread.sleep(100);} catch (InterruptedException e) {}
        }
    });

或者自己創建線程(首先是 D 線程),並在每個 (ABC) 工作結束時詢問 D 是否正在運行,如果沒有,則啟動它。

有很多解決方案,復雜性大相徑庭,它們是否合適完全取決於您的上下文。

要使用信號量等待 3 個線程之一完成,在啟動第 4 個不同的線程之前,您可以將信號量初始化為0 在運行第 4 個線程之前,您獲得了信號量,但不能,因為它是0 當 3 個線程之一完成時,它釋放信號量,允許第 4 個線程運行。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Semaphore sem = new Semaphore(0);
        char[] letters = {'a', 'b', 'c'};
        List<Thread> threadList = new ArrayList<>();

        for (int threadNum = 0; threadNum < 3; threadNum++) {
            int finalThreadNum = threadNum;
            Thread t = new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    System.out.print(letters[finalThreadNum]);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                sem.release();
            });

            threadList.add(t);
            t.start();

        }


        Thread lastThread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.print('d');
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        sem.acquire();
        lastThread.start();

        for (Thread t : threadList) {
            t.join();
        }
        lastThread.join();
    }
}

您可以使用Semaphore 前 3 個線程將獲得所有許可,當第一個線程釋放許可時,第 4 個線程將獲取並運行。

public static void main(String [] args) throws IOException {
    Semaphore semaphore = new Semaphore(3, true);
    
    
    Runnable a = new Runnable() {   
        @Override
        public void run() {
            try {
                semaphore.acquire();
                for (int i = 0; i < 50; i++) {
                    System.out.println("A");
                }                   
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
            }
        }
    };
    Runnable b = new Runnable() {   
        @Override
        public void run() {
            try {
                semaphore.acquire();
                for (int i = 0; i < 50; i++) {
                    System.out.println("B");
                }                   
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
            }
        }
    };
    Runnable c = new Runnable() {   
        @Override
        public void run() {
            try {
                semaphore.acquire();
                for (int i = 0; i < 50; i++) {
                    System.out.println("C");
                }                   
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
            }
        }
    };
    
    Runnable d = new Runnable() {   
        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println("D");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
            }
        }
    };
    
    ExecutorService exec = Executors.newFixedThreadPool(5);
    exec.execute(a);
    exec.execute(b);
    exec.execute(c);
    exec.execute(d);
}

如果第 4 個線程的 semaphore.acquire() 發生在其他 3 個線程的獲取之前怎么辦...

您可以為此使用CountDownLatch 基於@Ryan 的回答:

    public static void main(String [] args) throws IOException {
        Semaphore semaphore = new Semaphore(3, true);
        CountDownLatch okToStartTaskD = new CountDownLatch(3);
        
        Runnable a = new Runnable() {   
            @Override
            public void run() {
                try {
                    semaphore.acquire();
                    okToStartTaskD.countDown();
                    ...
                }
                ...
            }
        };
        Runnable b = new Runnable() {   
            @Override
            public void run() {
                try {
                    semaphore.acquire();
                    okToStartTaskD.countDown();
                    ...                 
                }
            ...
            }
        };
        ...
        
        ExecutorService exec = Executors.newFixedThreadPool(5);
        exec.execute(a);
        exec.execute(b);
        exec.execute(c);
        try {
            okToStartTaskD.await();
        }
        catch (InterruptedException ex) {
            ex.printStackTrace();
            ...should never get here, but what if?...
        }
        exec.execute(d);
    }

暫無
暫無

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

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