如何知道两个线程中哪个线程先完成执行

3

我有两个线程 A 和 B。如果 A 线程先完成,我需要执行函数1;如果 B 线程先完成,我需要执行函数2。我如何知道哪一个线程先完成了执行?


可能是重复的问题:需要在多线程中帮助确定哪个线程先停止 - dogbane
3个回答

2

您可以使用以下代码,它只会在前一个值为null时才被设置。(即使只有一个线程,也可以使用此方法来确保值在设置后不会更改)

AtomicReference<ValueType> ref = new AtomicReference<ValueType>();

ref.compareAndSet(null, value);

这是一个不错的解决方案,但它不允许主线程等待结果。 - jtahlborn
不过,join() 可以做到这一点,并且非常标准化。如果您想避免使用 join(),等待线程可以忙等待,或者您可以使用 BlockingQueue 的 add() 和 take() 方法获取第一个答案,而不必等待其他线程完成。 - Peter Lawrey

2
import java.util.concurrent.Semaphore;

public class ThreadTest {
    private Semaphore semaphore = new Semaphore(0);
    private String winner;

    private synchronized void finished(String threadName) {
        if (winner == null) {
            winner = threadName;
        }
        semaphore.release();
    }

    public void run() {
        Runnable r1 = new Runnable() {
            public void run() {
                try {
                    Thread.sleep((long) (5000 * Math.random()));
                }
                catch (InterruptedException e) {
                    // ignore
                }
                finally {
                    finished("thread 1");
                }
            }
        };

        Runnable r2 = new Runnable() {
            public void run() {
                try {
                    Thread.sleep((long) (5000 * Math.random()));
                }
                catch (InterruptedException e) {
                    // ignore
                }
                finally {
                    finished("thread 2");
                }
            }
        };

        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        t1.start();
        t2.start();
        try {
            semaphore.acquire();
            System.out.println("The winner is " + winner);
        }
        catch (InterruptedException e) {
            System.out.println("No winner");
            Thread.currentThread().interrupt();
        }
    }

    public static void main(String[] args) {
        new ThreadTest().run();
    }
}

这种解决方案的优点在于,只要第一个线程完成就有了胜者,而无需等待所有线程都完成。
编辑:正如jtahlborn所示,CountDownLatch是此问题的更好抽象。因此,可以将相同的算法编写为以下内容:
import java.util.concurrent.CountDownLatch;

public class ThreadTest {
    private CountDownLatch latch = new CountDownLatch(1);
    private String winner;

    private synchronized void finished(String threadName) {
        if (winner == null) {
            winner = threadName;
        }
        latch.countDown();
    }

    public void run() {
        Runnable r1 = new Runnable() {
            public void run() {
                try {
                    Thread.sleep((long) (5000 * Math.random()));
                }
                catch (InterruptedException e) {
                    // ignore
                }
                finally {
                    finished("thread 1");
                }
            }
        };

        Runnable r2 = new Runnable() {
            public void run() {
                try {
                    Thread.sleep((long) (5000 * Math.random()));
                }
                catch (InterruptedException e) {
                    // ignore
                }
                finally {
                    finished("thread 2");
                }
            }
        };

        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        t1.start();
        t2.start();
        try {
            latch.await();
            System.out.println("The winner is " + winner);
        }
        catch (InterruptedException e) {
            System.out.println("No winner");
            Thread.currentThread().interrupt();
        }
    }

    public static void main(String[] args) {
        new ThreadTest().run();
    }
}

如果你想在展示获胜者之前等待两个线程完成,你只需要将门闩的初始值从1改为2。


我本来想将这个回答评为好的,但我不确定为什么你使用了信号量而不是简单的等待/通知。 - jtahlborn
@jtahlborn:因为Semaphore提供了比synchronized/wait/notify更高的抽象层次,所以更安全、更易于使用和理解。 - JB Nizet
1
实际上,我认为Semaphore是一个更容易被滥用的混淆抽象。Semaphore是用于资源分配的。在这种情况下使用它比简单的wait/notify更加令人困惑。如果你想要一个更高级别的抽象,CountDownLatch可能更合适。 - jtahlborn

2

许多解决方案之一是,有一个共享的标志变量。

import java.util.concurrent.atomic.AtomicInteger;

final AtomicInteger winner = new AtomicInteger(0);

然后在第一个线程的run()方法调用结束时

winner.compareAndSet(0, 1);

在第二个线程中
winner.compareAndSet(0, 2);

这样,原子整数将只允许在调用compareAndSet的线程中设置非零值。
然后,您可以通过以下方式获得赢家索引的执行结果:
winner.get()

在生产代码中,我建议使用一些常量来作为初始值和线程索引。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接