繁体   English   中英

Java线程-第一个线程完成时关闭其他线程

[英]Java threads - close other threads when first thread completes

乡亲,所以我的问题是我有3个线程。

1Thread(Bot1)

public class Bot1 implements Runnable {
  String name;

  public Bot1(String s) throws Exception{
    ChatterBotFactory factory = new ChatterBotFactory();
    ChatterBot bot1 = factory.create(ChatterBotType.CLEVERBOT);
    ChatterBotSession bot1session = bot1.createSession();
    name=s;
    name=bot1session.think(s);  
  }

  public void run(){
    System.out.println("b1: "+name);
  }
}

其他都一样。 只有名字是Bot2Bot3 但是代码几乎相同。 我需要同时启动这些机器人。 我只需要显示最快的句子。 示例:如果Bot1显示“ Hello”的速度比Bot2Bot3快,那么我需要关闭Bot2Bot3线程。 但是我怎么看哪一个更快呢? 我需要关闭哪两个并再次运行我的代码? 希望您能理解我并能帮助我。 谢谢,抱歉我的英语不好。

您可以使用两个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();
   }

我会尝试在每个漫游器中都具有一个boolean isRunning ,并且在run方法中具有while(isRunning) 然后在第四个线程中,检查所有的机器人,看看是否完成了。 如果已完成,请将其他机器人的isRunning设置为false ,然后应退出。

由于您所有的Bot类都具有相同的代码,因此只创建一个Bot类,并创建三个对象bot1,bot2,bot3。 将它们传递给线程构造函数以创建三个线程。

让一个类变量说,布尔值访问=假。 也有一个共享的类授予读/写锁定。 Bot类的内部运行方法,如下所示:

run(){
Lock();
if(accessed){
return;
}
syso("hello");
accessed = true;

Unlock();
}

首先到达的线程将具有锁,并将更改所访问的变量,其余的将在发现该变量设置为true时返回。

我没有对此进行测试,但希望它可以编译并为您提供一种实现方法的想法。

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;

final class Bot
  extends Thread
{

  private final CountDownLatch latch;

  private final AtomicReference<Bot> winner;

  Bot(String name, CountDownLatch latch, AtomicReference<Bot> winner)
  {
    super(name);
    this.latch = latch;
    this.winner = winner;
  }

  @Override
  public void run()
  {
    try {
      latch.await();
      winner.compareAndSet(null, this);
    }
    catch (InterruptedException ignore) {
      /* This thread was told to stop before completing its work. */
    }
  }

  private static final int BOT_COUNT = 3;

  public static void main(String[] argv)
    throws Exception
  {
    CountDownLatch latch = new CountDownLatch(1);
    AtomicReference<Bot> ref = new AtomicReference<>();
    Set<Bot> losers = new HashSet<>();
    for (int i = 0; i < BOT_COUNT; ++i) {
      Bot b = new Bot("Bot" + i, latch, ref);
      losers.add(b);
      b.start();
    }
    latch.countDown();
    for (Bot b : losers)
      b.join();
    Bot winner = ref.get();
    if (winner == null)
      System.out.println("No winner.");
    else {
      losers.remove(winner);
      System.out.println("Winner: " + winner.getName());
      for (Bot loser : losers)
        System.out.println("  Loser: " + loser.getName());
    }
  }

}

可以控制线程启动并确保只有一个“胜利”的另一种选择是使用BlockingQueue 但是,朝着这个方向发展更能说明一种更好的方法是将ExecutorService与缓存的线程一起使用。

我在寻找有关如何使用最快线程的结果的问题的答案时找到了这个问题。 但是, 我收到的最佳答案 (由VGR编写)也可以同样很好地应用于该问题(我原本以为有不同的解决方案),所以我认为我会这样做。

IMO这种类型的问题是使用ExecutorService.invokeAny()方法的好地方。 确实,您不是要立即停止所有线程。 相反,您尝试使用最快完成线程的结果,然后只是忽略后续计算,如果可以,则暂停,但这是第二要点。 以下代码取自@lreeder的答案 ,但使用前面提到的invokedAny()方法进行了简化。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class InvokeAnyThreadController {
  public static void main(String[] args) throws Exception {
    new InvokeAnyThreadController().threadController();
  }

  public void threadController() throws Exception {
    int numWorkers = 20;

    List<Worker> workerList = new ArrayList<>(numWorkers);
    for (int i = 0; i < numWorkers; i++) {
      Worker worker = new Worker(i);
      workerList.add(worker);
    }

    ExecutorService execSvc = Executors.newFixedThreadPool(numWorkers);
    int firstInt = execSvc.invokeAny(workerList);
    System.out.println("firstInt=" + firstInt);
  }

  private static class Worker implements Callable<Integer> {

    private final int id;

    public Worker(int id) {
      this.id = id;
    }

    @Override
    public Integer call() {
      return this.id;
    }
  }
}

此代码使用Callable接口(而不是Runnable接口),以便可以返回值。 确保对invokeAny()的调用返回快速计算的值,并丢弃所有其他线程。 然后, invokeAny()调用将尽快停止所有其他线程,但是,其他几个线程也有可能也将完成计算,尽管它们的结果将被忽略并且不会返回。 使用此方法,无需使用较低级别的线程或其他类,例如SemaphoresCountDownLatchesFutureTasks等。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM