4

我写了一个类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)fromCountDownLatch类。

我希望每个倒计时锁存器具有相同的超时时间,并且该方法总体上只是为了阻止timeoutTimeUnits 的数量。我很难做到这一点。我所拥有的是:

  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;
  }

因此,总超时得到处理,但每次倒计时只占总时间的百分比。

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

这里有一个可视化。感谢 hexafraction 的 ascii 艺术灵感。

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

3 回答 3

6

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

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

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

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

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

于 2013-09-06T17:38:00.593 回答
3

Hexafraction 的答案是正确的,而且比我要写的要理智得多,但你似乎出于某种原因不喜欢它。我会把这个写成评论,对所说的答案,但它太长了,需要一些格式。去接受他的回答,不是这个。

用他的回答:

L1 获得 8 秒并在 t1 结束。

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

L3 获得 8 - t2 秒,加上您在 L1 和 L2 上等待时的 t2 秒。

...

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

请注意,所有这些加起来为 8 秒。每个latch 有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); 
}

您似乎对超时调用不完全相同这一事实感到困惑 - 但如果您要连续执行它,它们就不可能,而且它们不需要。它们是一个实现细节——每个锁存器从调用开始仍然有 8 秒的时间来运行,这就是 await(...) 的合同所说的。你请求了一个 8 秒的超时,你得到一个 8 秒的超时。如果所有的锁存器都被触发,它就会成功,如果没有,它就会失败,这就是你所期望的。

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

如果这些都没有回答,您可能需要返回编辑以解释您的真正意思,因为我怀疑您遗漏了一些东西。

于 2013-09-07T00:30:41.037 回答
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 及时:false
MultiWaiter#1 已释放!

MultiWaiter#0 及时发布:真正的
MultiWaiter#0 已发布!
MultiWaiter#2 及时发布:真正的
MultiWaiter#2 已发布!

于 2013-09-06T22:36:02.667 回答