我正在阅读B. Goetz Java Concurrency In Practice,现在我在关于同步器的部分。他将latches
描述为一种同步器,并为CountDownLatch
提供了典型的用例:
public class TestHarness{
public long timeTask(int nThreads, final Runnable task){
final CountDownLatch startGate = new CountDownLatch(1);
final CountDownLatch endGate = new CountDownLatch(nThreads);
for(i = 0; i < nThreads; i++) {
Thread t = new Thread(){
try{
startGate.await();
try{
task.run();
} finally {
endGate.countDown();
}
} catch (InterruptedException ignored){ }
};
t.start(); // <--- Here
}
long start = System.nanoTime();
startGate.countDown();
endGate.await();
long end = System.nanoTime();
return end - start;
}
}
在这个例子中,锁存器的使用情况非常清楚,但问题是
为什么程序可能会被视为正确的同步 ?
Accoridng to JLS 17.4:
一个实现可以自由地生成它喜欢的任何代码,只要全部 由此产生的程序执行产生的结果可以是 由记忆模型预测。
可能会发生一些重新排序。我们这里没有任何明确的同步块。例如,为什么编译器在循环之前产生startGate.countDown
的代码是不可能的。
答案 0 :(得分:2)
看起来原版并不能保证所有线程都在准备就绪之前就像@ St.Antario所提到的那样。它确保在代码long start = System.nanoTime();
有些线程可以在初始化所有线程之前开始运行。我认为如果代码想要阻止所有线程启动直到它们都准备就绪,那么代码必须是:
public class TestHarness{
public long timeTask(int nThreads, final Runnable task){
final CountDownLatch startGate = new CountDownLatch(nThreads);//Changed this from 1
final CountDownLatch endGate = new CountDownLatch(nThreads);
for(i = 0; i < nThreads; i++) {
Thread t = new Thread(){
try{
startGate.countDown(); //Reduce the latch count by 1
startGate.await(); //Once the last Thread is ready, this will continue
try{
task.run();
} finally {
endGate.countDown();
}
} catch (InterruptedException ignored){ }
};
t.start(); // <--- Here
}
long start = System.nanoTime();
endGate.await();
long end = System.nanoTime();
return end - start;
}
}
这是一个测试工具,显示原始代码中的init和start序列:
import java.util.concurrent.CountDownLatch;
public class TestHarness{
public long timeTask(int nThreads, final Runnable task) throws InterruptedException{
final CountDownLatch startGate = new CountDownLatch(1);
final CountDownLatch endGate = new CountDownLatch(nThreads);
for(int i = 0; i < nThreads; i++) {
Thread t = new Thread( new Runnable(){
@Override
public void run() {
try{
System.out.println("Init");
startGate.await();
try{
task.run();
} finally {
endGate.countDown();
}
} catch (InterruptedException ignored){ }
};
});
t.start(); // <--- Here
}
long start = System.nanoTime();
startGate.countDown();
System.out.println("Open Gate");
endGate.await();
long end = System.nanoTime();
return end - start;
}
public static void main(String[] args) throws Exception {
new TestHarness().timeTask(10, new Runnable() {
@Override
public void run() {
System.out.println("Am Running");
}
});
}
}
如果您运行代码,有时您会看到:
Init
Open Gate
Am Running
Init
这意味着在调用startGate.countDown()之前,某些线程可能没有准备好/创建。
答案 1 :(得分:0)
为什么不可能,例如,编译器生成一个代码,其中startGate.countDown位于循环之前。
有可能。只是countDown()
才会完成,直到其他线程都调用了countDown()
。