等待容器中的CountDownLatch的多个实例

时间:2013-09-06 17:34:44

标签: java concurrency java.util.concurrent

我写了一个类CountDownLatchContainer,它有一个await()方法,等待所有CountDownLatches。一切都很好。

public final class CountDownLatchContainer
{
  private final Set<CountDownLatch> countDowns;

  public CountDownLatchContainer(List<CountDownLatch> countDowns)
  {
    this.countDowns = new HashSet<CountDownLatch>(countDowns);
  }

  public void await() throws InterruptedException
  {
    for (CountDownLatch count : countDowns)
      count.await();
  }
}

为了科学和艺术(:D),我想扩展功能并添加public boolean await(long timeout, TimeUnit unit)类的CountDownLatch

我希望每个倒计时锁存器具有相同的超时和整个方法,以阻止timeout TimeUnits的数量。我很难实现这一目标。我所拥有的是:

  public boolean await(long timeout, TimeUnit unit) throws InterruptedException
  {
    boolean success = true;
    for (CountDownLatch count : countDowns)
      success &= count.await(timeout / countDowns.size(), unit);
    return success;
  }

因此,总体超时得到了解决,但每次倒计时仅占整个时间的百分比。

那么如何在不超过总时间的情况下为单个锁存器提供与方法参数中指定的时间相同的时间?

这里是一个可视化。对ascii艺术灵感的六重奏。

|-----------------------------|
             Total
|-----------------------------|
  L1       
|-----------------------------|
  L2       
|-----------------------------|

3 个答案:

答案 0 :(得分:6)

等待每个闩锁工作,然后查看剩余时间。

首先等待原始时间的第一个锁存器。注意实际花了多长时间。剩余时间设置为总减去时间。现在,等待剩余时间限制的下一个锁存器。减去实际。继续,直到等待所有锁存器或剩余时间达到0(由于其剩余时间内的锁存超时)

|-----------------------------|
              Total
|------|----------------------|
  L1       Remaining(1)
|------|---------|------------|
  L1     L2        Remaining(2)

依此类推...等待l1为总数,l2为剩余(1)等等

另一种方法是将操作重新分段为切片,例如每个1毫秒。循环锁存器(将它们标记为使用/通过集合的可变副本的迭代器移除它们可能是好的)并等待切片长度,直到释放所有锁存器或达到时间。

答案 1 :(得分:3)

Hexafraction的答案是正确的,并且比我即将写的要灵犀得多,但你似乎并不喜欢它。我会写这个作为评论,在所说的答案,但它太长了,需要一些格式。去接受他的回答,而不是这个。

回答:

L1获得8秒并在t1结束。

L2获得8 - t1秒,加上你在L1等待时的t1秒,并在t2结束。

当你在L1上等待L2时,L3会得到8秒到2秒,加上t2秒。

...

L_N获得8 - t_n-1秒,加上等待前一任务的t_n-1秒。

请注意,所有这些加起来最多为8秒。从等待呼叫开始,每个锁存器都有8秒钟。最后,A)所有内容都已完成,并且您在最后一个完成后立即返回,或者B)一个或多个任务未在超时之前完成。

它在逻辑上等同于以下,但恕我直言,更清洁的代码:

CountDownLatch[] latches;

public void await(final int timeout, final TimeUnit unit) {

  final CountDownLatch metaLatch = new CountDownLatch(latches.length);
  for (final int i = 0; i < latches.length; i++) {
     new Runnable() {
       public void run() {
         latches[i].await(timeout, unit);
         metaLatch.countDown();
       }
     }.start();
  }

  metaLatch.await(timeout, unit); 
}

您似乎对超时调用不完全相同这一事实感到困惑 - 但如果您要按顺序执行此操作,它们就不可能,并且它们不需要。它们是一个实现细节 - 每个锁存器从调用开始时仍然可以运行8s,这就是等待合同(...)所说的。您请求8秒超时,并且您获得8秒超时。如果所有锁存器都被触发,它会成功,如果没有,它会失败,这就是你所期望的。

如果你想像上面那样并行执行(加上我为了简洁而省略的错误处理),你可以,但老实说,这对于完成与hexa的答案相同的代码来说是不必要的复杂

如果没有一个回答它,你可能需要回去编辑来解释你的真实含义,因为我怀疑你要留下一些东西。

答案 2 :(得分:0)

我想我设法实现了await(long timeout, TimeUnit unit)的非阻塞容器版本。

public final class CountDownLatchContainer {
    private final Set<CountDownLatch> countDowns;

    public CountDownLatchContainer(List<CountDownLatch> countDowns) {
        this.countDowns = new HashSet<CountDownLatch>(countDowns);
    }

    public void await() throws InterruptedException {
        for (CountDownLatch count : countDowns)
            count.await();
    }

    public boolean await(long timeout, TimeUnit unit)
            throws InterruptedException{
        boolean success = true;
        ExecutorService e = Executors.newFixedThreadPool(countDowns.size());
        List<Future<Boolean>> futures = new ArrayList<Future<Boolean>>(countDowns.size());

        for (CountDownLatch count : countDowns)
            futures.add(e.submit(new SingleLatchTimeOutAwaiter(count, timeout, unit)));

        for (Future<Boolean> f : futures) {
            try {
                success &= f.get();
            } catch (ExecutionException e1) {
                throw new InterruptedException();
            }
        }
        e.shutdown();
        return success;
    }

    private static class SingleLatchTimeOutAwaiter implements Callable<Boolean> {
        private final CountDownLatch count;
        private final long timeout;
        private final TimeUnit unit;

        private SingleLatchTimeOutAwaiter(CountDownLatch count, long timeout,
                TimeUnit unit) {
            this.count = count;
            this.timeout = timeout;
            this.unit = unit;
        }

        public Boolean call() throws Exception {
            return count.await(timeout, unit);
        }
    }
}

这是一个测试案例。这很准确。

public class CountDownLatchContainerTest {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService e = Executors.newFixedThreadPool(3);
        CountDownLatch c1 = new CountDownLatch(1);
        CountDownLatch c2 = new CountDownLatch(1);

        CountDownLatchContainer c = new CountDownLatchContainer(Arrays.asList(
                c1, c2));
        e.submit(new MultiWaiter(c, 4, TimeUnit.SECONDS));
        Thread.sleep(1000);
        e.submit(new MultiWaiter(c, 2, TimeUnit.SECONDS));
        e.submit(new MultiWaiter(c, 3, TimeUnit.SECONDS));

        c1.countDown();
        int hundredth = 29; // 1/100 s delay
        for (int i = hundredth; i --> 0;) {
            Thread.sleep(100);
        }
        c2.countDown();
        e.shutdown();
    }

    private static class MultiWaiter implements Callable<Void> {
        private static final AtomicInteger instanceCounter = new AtomicInteger(
                0);
        private final int instance;

        private final CountDownLatchContainer c;
        private final long timeout;
        private final TimeUnit unit;

        private MultiWaiter(CountDownLatchContainer c, long timeout,
                TimeUnit unit) {
            this.c = c;
            this.timeout = timeout;
            this.unit = unit;
            this.instance = instanceCounter.getAndIncrement();
        }

        public Void call() throws Exception {
            System.out.println(getClass().getSimpleName() + "#" + instance
                    + " is waiting...");
            System.out.println(getClass().getSimpleName() + "#" + instance
                    + " is in time: " + c.await(timeout, unit));
            System.out.println(getClass().getSimpleName() + "#" + instance
                    + " is released!");
            return null;
        }
    }
}

<强>输出

  

MultiWaiter#0正在等待......

  MultiWaiter#1正在等待...
  MultiWaiter#2正在等待......所有人   MultiWaiter#1及时:假
  MultiWaiter#1被释放!

  MultiWaiter#0及时:真实   MultiWaiter#0被释放!
  MultiWaiter#2及时:真实   MultiWaiter#2发布了!