倒计时批处理 vs. 信号量

使用有什么好处吗?

java.util.concurrent.CountdownLatch

而不是

java.util.concurrent.Semaphore

据我所知,以下片段几乎是等效的:

1. 信号量

final Semaphore sem = new Semaphore(0);
for (int i = 0; i < num_threads; ++ i)
{
  Thread t = new Thread() {
    public void run()
    {
      try
      {
        doStuff();
      }
      finally
      {
        sem.release();
      }
    }
  };
  t.start();
}

sem.acquire(num_threads);

2:倒计时

final CountDownLatch latch = new CountDownLatch(num_threads);
for (int i = 0; i < num_threads; ++ i)
{
  Thread t = new Thread() {
    public void run()
    {
      try
      {
        doStuff();
      }
      finally
      {
        latch.countDown();
      }
    }
  };
  t.start();
}

latch.await();

除了在#2的情况下,闩锁不能重复使用,更重要的是,您需要提前知道将创建多少个线程(或等到它们全部启动后再创建闩锁)。

那么,在什么情况下,闩锁可能更可取呢?


答案 1

CountDownLatch经常用于与您的示例完全相反的情况。通常,您会有许多线程阻塞,当 countown 达到零时,这些线程将同时启动。await()

final CountDownLatch countdown = new CountDownLatch(1);

for (int i = 0; i < 10; ++ i) {
   Thread racecar = new Thread() {    
      public void run() {
         countdown.await(); //all threads waiting
         System.out.println("Vroom!");
      }
   };
   racecar.start();
}
System.out.println("Go");
countdown.countDown();   //all threads start now!

您还可以将其用作 MPI 样式的“屏障”,该屏障会导致所有线程在继续之前等待其他线程赶上某一点。

final CountDownLatch countdown = new CountDownLatch(num_thread);

for (int i = 0; i < num_thread; ++ i) {
   Thread t= new Thread() {    
      public void run() {
         doSomething();
         countdown.countDown();
         System.out.printf("Waiting on %d other threads.",countdown.getCount());
         countdown.await();     //waits until everyone reaches this point
         finish();
      }
   };
   t.start();
}

总而言之,可以按照您在示例中所示的方式安全地使用它们。CountDownLatch


答案 2

CountDownLatch 用于启动一系列线程,然后等待所有线程都完成(或直到它们调用给定的次数)。countDown()

信号量用于控制使用资源的并发线程数。该资源可以是类似于文件的内容,也可以是 CPU,方法是限制执行的线程数。信号量的计数可以随着不同的线程调用 和 而上升和下降。acquire()release()

在您的示例中,您实际上是在使用信号量作为一种 CountUP闩锁。鉴于您的意图是等待所有线程完成,使用 可以使您的意图更清晰。CountdownLatch


推荐