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

时间:2013-10-20 16:55:54

标签: java multithreading fclose

伙计所以我的问题是我有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主题。 但我怎么看哪一个更快?我需要关闭哪两个并再次运行我的代码?我希望你理解我并且可以帮助我。谢谢,抱歉我的英语不好。

5 个答案:

答案 0 :(得分:2)

您可以使用两个CountDownLatches和一个Semaphore。第一个倒计时锁存器同步线程启动,以便所有线程可以同时启动。第二个倒计时锁存器会在其中一个线程完成时通知您。信号量只允许获胜线程完成,防止竞争条件,其中一些其他线程可能会在您询问哪个线程是赢家时完成。您还需要在Bot类中添加某种已完成的标志,这样主线程就可以先判断哪一个已完成,因为运行方法可能无法及时退出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();
   }

答案 1 :(得分:0)

我会尝试在每个机器人中使用boolean isRunning,并且在run方法中有while(isRunning)。然后在第四个线程中,检查所有机器人,看看是否有任何机器人。完成后,将其他机器人的isRunning设置为false,然后退出。

答案 2 :(得分:0)

由于你的所有Bot类都有相同的代码,只需要制作一个类Bot并制作三个对象bot1,bot2,bot3。将它们传递给线程构造函数以生成三个线程。

创建一个类变量,例如,boolean accessible = false。还有一个授予读/写锁的共享类。 Bot类的内部运行方法,有这样的东西:

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

Unlock();
}

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

答案 3 :(得分:0)

我没有对此进行测试,但希望它可以编译并让您了解一种方法。

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

答案 4 :(得分:0)

我在搜索clone的答案时发现了这个问题。但是,my own question on how to use the result from the fastest thread(由the best answer that I received编写)同样可以很好地应用于这个问题(我原本认为有一个不同的解决方案)所以我认为我会这样做。

IMO这类问题是使用VGR方法的好地方。真的,你不是要立即停止所有线程。相反,您正在尝试使用最快完成的线程的结果,然后忽略后续计算,如果可以,则暂停,但这是次要问题。以下代码取自ExecutorService.invokeAny(),但使用前面提到的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; } } } 方法进行了简化。

invokeAny()

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

相关问题