假设我有一个Master,它保存SlaveThread对象的列表。在每个时间步,我希望Master并行运行SlaveThreads,但是,在时间步的结束时,我希望SlaveThreads在向前推进之前等待彼此完成当前时间步。另外,我不想在每个时间步重新实现SlaveThreads。我有两个可能的解决方案,我不知道如何使它们中的任何一个工作:
1)SlaveThread中的run()方法处于while(true)循环中。在SlaveThread中执行单个循环之后,我将让SlaveThread通知Master(我不知道该怎么做),而Master会做类似的事情
try{
for (int i = 0; i < numSlaveThreads; i++) {
while (!slaveThreads[i].getCompletedThisIter()) {
wait()
}
}
System.out.println("Joined");
}
在进入下一个时间步之前。我该怎么做?如何让一个SlaveThread只通知主服务器?
2)Slave中的run()不在while(true)循环中,然后我必须在每次迭代时调用start()。但是此时Slave的线程状态将被终止。如何在不重新实例化的情况下再次调用start()?
答案 0 :(得分:5)
这正是障碍的所在,您可以通过CyclicBarrier或CountDownLatch实现这一目标。这些是同步器,用于延迟线程的进度,直到达到所需的状态,在这种情况下,线程已完成计算。
这取决于你想要实现的细节:
锁存器用于等待事件;障碍是等待其他的 线程。
对于以下列方式完成的CyclicBarrier
:
// whereby count is the number of your slave threads
this.barrier = new CyclicBarrier(count);
然后在您的奴隶的Runnable
定义中,您将在计算结束时插入:barrier.await()
public class Slaves implements Runnable {
// ...
@Override
public void run() {
while(condition) {
// computation
// ...
try {
// do not proceed, until all [count] threads
// have reached this position
barrier.await();
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
}
}
在所有线程完成计算之前,您的从属线程将不会继续。这样您就不需要在另一个主线程之间实现信令。
但是,如果您想要在所有线程到达该位置(主信令)后执行某些代码,则可以将另外的Runnable
传递给CyclicBarrier
构造函数,该构造函数将在执行之后执行所有线程都到达了障碍。
this.barrier = new CyclicBarrier(count,
new Runnable() {
@Override
public void run() {
// signal your master thread, update values, etc.
}
}
);
答案 1 :(得分:3)
您可以使用ExecutorService的组合来管理您的线程(即,您可以回收线程而无需在每个周期创建新线程)和CyclicBarrier来同步所有从属。< / p>
请参阅下面的一个简单示例,其中主服务器在循环中启动从服务器,确保它们在重新启动之前完成所有操作。奴隶,有点懒,只是睡觉一些(不是真正的随机)时间:
public class Test {
private static final ExecutorService executor = Executors.newFixedThreadPool(5);
private static final CyclicBarrier barrier = new CyclicBarrier(5); //4 slaves + 1 master
public static void main(String[] args) throws InterruptedException {
Runnable master = new Runnable() {
@Override
public void run() {
try {
while (true) {
System.out.println("Starting slaves");
for (int i = 100; i < 500; i += 100) {
executor.submit(getRunnable(i));
}
barrier.await();
System.out.println("All slaves done");
}
} catch (InterruptedException | BrokenBarrierException ex) {
System.out.println("Bye Bye");
}
}
};
executor.submit(master);
Thread.sleep(2000);
executor.shutdownNow();
}
public static Runnable getRunnable(final int sleepTime) {
return new Runnable() {
@Override
public void run() {
try {
System.out.println("Entering thread " + Thread.currentThread() + " for " + sleepTime + " ms.");
Thread.sleep(sleepTime);
System.out.println("Exiting thread " + Thread.currentThread());
barrier.await();
} catch (BrokenBarrierException | InterruptedException ex) {
}
}
};
}
}