关于CountDownLatch的一个问题

light_sky 2013-04-28 08:00:46
不知道为什么下面的代码没有执行循环,只执行了一次?请大侠帮忙解答,十分感谢


import java.util.concurrent.CountDownLatch;

class Driver{

public static void main(String[] args) {
CountDownLatch startSignal = new CountDownLatch(1);//启动信号
CountDownLatch doneSignal = new CountDownLatch(3);//记录3个工人的状态

try {
for(int i=0;i<3;i++){
System.out.println("************"+i);

//调用3个工人工作,虽然调用了start方法,但是worker的内部
//使用了CountDownLatch.await(),所以需要countDown(),才能工作
new Thread(new Worker(startSignal,doneSignal)).start();

System.out.println("经理即将发布任务");
startSignal.countDown();//让所有的工人开始工作

System.out.println("经理已发布任务,等待员工提交……");

doneSignal.await();//等待所有的工人都完成
System.out.println("所有工作完成");
}
}catch(Exception e){
e.printStackTrace();
}

}
}

class Worker implements Runnable{
CountDownLatch startSignal;
CountDownLatch doneSignal;
Worker(CountDownLatch startSignal,CountDownLatch doneSignal){
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}

void doWork(){
System.out.println("线程" +Thread.currentThread().getName()+"已接收命令,开始执行!");
};

@Override
public void run() {
try {
System.out.println("线程"+Thread.currentThread().getName()+"已准备完毕");
startSignal.await();//工人处于就绪状态

doWork();
doneSignal.countDown();//每个工人做完自己的工作,就去提交
System.out.println("线程"+Thread.currentThread().getName()+"完成任务,已提交");
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
...全文
255 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
dracularking 2013-04-29
  • 打赏
  • 举报
回复
引用 5 楼 xushuaic 的回复:
这样看来,API给的示例是错误的了?
API给的示例,它的await不在for循环内
light_sky 2013-04-28
  • 打赏
  • 举报
回复
又看了遍代码,发现自己上面理解的不对。应该是只有一个线程被创建了,后面两个还不能执行循环,因为计数器为3,而现在只有1,所以就阻塞在循环部了
light_sky 2013-04-28
  • 打赏
  • 举报
回复
这样看来,API给的示例是错误的了?
light_sky 2013-04-28
  • 打赏
  • 举报
回复
把await和countdown的语句放在for循环的外面可以了,原来是主线程执行了startSignal.countDown(),计数器-1,但只能执行到 doneSignal.await();//等待所有的工人都完成 就阻塞了,因为都是在循环内部,所以被创建的3个子线程并不能去执行自己的doneSignal.countDown(); ,导致了阻塞。 非常谢谢两位的回答
dracularking 2013-04-28
  • 打赏
  • 举报
回复
引用 2 楼 xushuaic 的回复:
这是另一个例子,可以得到正确结果,CountDownLatch是不可重用,不过跟是否在循环内部创建应该没有关系吧,只要保证它们是同一个CountDownLatch,就可以了,上面那个Driver的例子里,通过构造函数传递给Worker,应该都是同一个CountDownLatch,我也是根据API里的示例,照葫芦画瓢画出来的,自己实在看不出两个例子有什么区别,不都是通过Runable对象启动么,是不是就是通过构造函数传递CountDownLatch那里出了问题,还请能够做进一步解答,感激不尽
1楼说到对的,如果你在循环内await,会阻塞在第一次循环中,那种情况下,就把后面线程创建的机会给剥夺了 2楼程序可以,是因为程序中的for循环不是单线程执行的
light_sky 2013-04-28
  • 打赏
  • 举报
回复
这是另一个例子,可以得到正确结果,CountDownLatch是不可重用,不过跟是否在循环内部创建应该没有关系吧,只要保证它们是同一个CountDownLatch,就可以了,上面那个Driver的例子里,通过构造函数传递给Worker,应该都是同一个CountDownLatch,我也是根据API里的示例,照葫芦画瓢画出来的,自己实在看不出两个例子有什么区别,不都是通过Runable对象启动么,是不是就是通过构造函数传递CountDownLatch那里出了问题,还请能够做进一步解答,感激不尽 API示例

class Driver { // ...
   void main() throws InterruptedException {
     CountDownLatch startSignal = new CountDownLatch(1);
     CountDownLatch doneSignal = new CountDownLatch(N);

     for (int i = 0; i < N; ++i) // create and start threads
       new Thread(new Worker(startSignal, doneSignal)).start();

     doSomethingElse();            // don't let run yet
     startSignal.countDown();      // let all threads proceed
     doSomethingElse();
     doneSignal.await();           // wait for all to finish
   }
 }

 class Worker implements Runnable {
   private final CountDownLatch startSignal;
   private final CountDownLatch doneSignal;
   Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
      this.startSignal = startSignal;
      this.doneSignal = doneSignal;
   }
   public void run() {
      try {
        startSignal.await();
        doWork();
        doneSignal.countDown();
} catch (InterruptedException ex) {} // return;
   }

   void doWork() { ... }
 }

我自己的另一个代码,可以正常运行

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchTest {
	public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();
		final CountDownLatch startSignal = new CountDownLatch(1);
		final CountDownLatch doneSignal = new CountDownLatch(3);
		
		for(int i=0;i<3;i++){
			Runnable runnable = new Runnable() {
				
				@Override
				public void run() {
					try {
						System.out.println("线程"+Thread.currentThread().getName()+"正在准备");
						startSignal.await();
						System.out.println("线程"+Thread.currentThread().getName()+"开始运行");
						Thread.sleep((long) (Math.random() * 10000));
						doneSignal.countDown();
						
						System.out.println("线程"+Thread.currentThread().getName()+"已运行完");
						
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
				}
			};
			service.execute(runnable);
		}
		
		try {
			Thread.sleep((long) (Math.random() * 10000));
				System.out.println("线程"+Thread.currentThread().getName()+"即将发出任务");
				startSignal.countDown();
				System.out.println("线程"+Thread.currentThread().getName()+"已发出任务,等待完成");
				doneSignal.await();
				System.out.println("所有任务都已完成");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
	}
	
}
andycpp 2013-04-28
  • 打赏
  • 举报
回复
CountDownLatch不可重复使用,当计数器减少到0之后,就废了,无法继续使用了。 你在循环外创建CountDownLatch,在循环内使用,肯定是不行的,创建语句也应该写到循环内

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧