1

伙计们,所以我的问题是我有 3 个线程。

1线程(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”的速度比Bot2and快Bot3,那么我需要关闭Bot2andBot3线程。但是我怎么看哪一个更快呢?我需要关闭哪两个并再次运行我的代码?我希望你能理解我并能帮助我。谢谢你,对不起我的英语不好。

4

5 回答 5

2

您可以使用两个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();
   }
于 2013-10-20T17:11:28.460 回答
0

我会尝试boolean isRunning在每个机器人中都有一个,并且在运行方法中有一个while(isRunning). 然后在第四个线程中,检查所有机器人,看看是否有任何完成。如果一个完成,将isRunning其他机器人的设置为false,他们应该退出。

于 2013-10-20T17:04:48.927 回答
0

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

让一个类变量说,布尔访问=假。还有一个共享类授予读/写锁。在 Bot 类的 run 方法里面,有这样的东西:

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

Unlock();
}

首先到达的线程将拥有锁,并将更改访问的变量,休息将在发现该变量设置为真时返回。

于 2013-10-20T17:05:17.710 回答
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缓存线程。

于 2013-10-20T18:28:56.077 回答
0

我在寻找我自己关于如何使用最快线程结果的问题的答案时发现了这个问题。但是,我收到的最佳答案(由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等。

于 2016-12-07T20:49:24.227 回答