您可以使用两个CountDownLatches和一个Semaphore。第一个倒计时锁存器同步线程启动,以便所有线程都可以同时启动。当其中一个线程完成时,第二个倒计时闩锁会通知您。信号量只允许获胜线程完成,从而防止在您询问哪个线程是获胜者时某些其他线程可能完成的竞争条件。您还需要在 Bot 类中添加某种已完成标志,以便主线程可以判断哪个先完成,因为 run 方法可能无法及时退出以进行isAlive()
检查。
请注意,同时启动的线程仍然取决于您的线程调度程序。这是一些示例代码:
创建和启动线程的线程控制器
public void threadController() throws Exception
{
int numWorkers = 20;
List<Worker> workerList = new ArrayList<Worker>(numWorkers);
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(1);
//Semaphore prevents only one thread from completing
//before they are counted
Semaphore pauseForCheck = new Semaphore(1);
for(int i=0; i<numWorkers; i++)
{
Worker worker = new Worker(i, startSignal, doneSignal, pauseForCheck);
Thread thread = new Thread(worker);
//worker has started, but will block on await();
thread.start();
workerList.add(worker);
}
//tell workers they can start
startSignal.countDown();
//wait for one thread to complete.
doneSignal.await();
//Look at all workers and find which one is done
for (int i=0; i< numWorkers; i++)
{
if(workerList.get(i).isCompleted())
{
System.out.printf("Thread %d finished first\n", i);
}
}
//add permits to semaphore so all losing threads can finish
pauseForCheck.release(numWorkers - 1);
}
实际工作的工人阶级
class Worker implements Runnable
{
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
private final Semaphore pauseForCheck;
private final int id;
private boolean completed = false;
public Worker(int id, CountDownLatch startSignal, CountDownLatch doneSignal, Semaphore pauseForCheck )
{
this.id = id;
this.startSignal = startSignal;
this.doneSignal = doneSignal;
this.pauseForCheck = pauseForCheck;
}
public boolean isCompleted()
{
return completed;
}
public void run()
{
try
{
//block until controller counts down the latch
startSignal.await();
//simulate real work
Thread.sleep((long) (Math.random() * 1000));
//try to get the semaphore. Since there is only
//one permit, the first worker to finish gets it,
//and the rest will block.
pauseForCheck.acquire();
}
catch (InterruptedException e)
{
//don't care about this
}
//Use a completed flag instead of Thread.isAlive because
//even though countDown is the last thing in the run method,
//the run method may not have before the time the
//controlling thread can check isAlive status
completed = true;
//tell controller we are finished
doneSignal.countDown();
}