【问题标题】:What is java's equivalent of ManualResetEvent? [duplicate]什么是 java 的 ManualResetEvent 等价物? [复制]
【发布时间】:2009-06-30 16:09:38
【问题描述】:

java 中的 ManualResetEvent 是什么?

【问题讨论】:

  • 为什么不看看另一个最佳答案?

标签: java .net multithreading synchronization


【解决方案1】:
class ManualResetEvent {

  private final Object monitor = new Object();
  private volatile boolean open = false;

  public ManualResetEvent(boolean open) {
    this.open = open;
  }

  public void waitOne() throws InterruptedException {
    synchronized (monitor) {
      while (open==false) {
          monitor.wait();
      }
    }
  }

  public boolean waitOne(long milliseconds) throws InterruptedException {
    synchronized (monitor) {
      if (open) 
        return true;
      monitor.wait(milliseconds);
        return open;
    }
  }

  public void set() {//open start
    synchronized (monitor) {
      open = true;
      monitor.notifyAll();
    }
  }

  public void reset() {//close stop
    open = false;
  }
}

【讨论】:

  • 看起来它根本不起作用:线程一调用waitOne()并阻塞monitor.wait()。线程两个调用set 并阻塞synchronized (monitor)。这是唯一的用例,对吧?我认为你可以在set 中省略synchronized 声明。
  • @LimitedAtonement:在 Java 中,您必须处于同步块中才能等待对象。在等待期间,该对象的监视器锁被释放,因此可以在另一个线程中获取它。见docs.oracle.com/javase/tutorial/essential/concurrency/…
  • @WayneUroda 我的错。我想我不应该问你为什么必须在synchronized 块中才能通过调用wait 来释放锁。我敢肯定那和山一样古老;)。
  • @LimitedAtonement 在其他语言中,您可以等待/通知类似实体,而无需锁定它们(C# IIRC、win32/GDI 等)。我认为在 java 中这样做是为了帮助程序员避免一些常见的竞争条件。也许这比我能解释得更好:) stackoverflow.com/questions/2779484/…
  • 它工作正常!及其在java中的正确实现ManualResetEvent。所有其他实现或糟糕或复杂
【解决方案2】:

我所知道的最接近的是Semaphore。只需将其与“许可”计数 1 一起使用,获取/释放将与您从 ManualResetEvent 中知道的几乎相同。

初始化为 1 的信号量,并且 使用它使其仅具有 大多数许可证可用,可以服务 作为互斥锁。这是 通常称为二进制 信号量,因为它只有两个 状态:一个许可证可用,或零 可用的许可证。用于此 方式,二进制信号量有 属性(不像许多 Lock 实现),“锁”可以 由线程以外的线程释放 所有者(因为信号量没有 所有权)。这在某些情况下可能很有用 专门的上下文,例如死锁 恢复。

【讨论】:

  • 我认为这在没有竞争条件的情况下是行不通的(假设线程无法释放它以前没有获得的信号量)
  • 我收回它 - 从文档中看来,信号量可以在不使用它的情况下释放。
  • 是的,那是因为信号量没有所有权概念。它们几乎是同步的计数器,如果计数器为 0,则线程等待。
  • 如果信号量在没有适当获取的情况下多次释放,那么availiblePermits 将大于1,并且它的行为方式与ManulResetEvent 不同,因为reset() 不会导致waitOne() 等待。
  • @Agent_L 你当然是对的,需要一个直接的release 来模仿ManualResetEvent,否则你有一个AutoResetEvent。正如我所写,这只是我所知道的 Java 中最接近的同步原语。 ManualResetEvent 的一个常见用途是让一个线程等待同步,并且该用例不会遭受微阻塞(这只会在多个线程同时等待事件时发生)并且开销应该是相当小(与另一个答案中提供的监视器解决方案相比)。
【解决方案3】:

尝试CountDownLatch 计数为一。

CountDownLatch startSignal = new CountDownLatch(1);

【讨论】:

  • CountDownLatch 的问题在于它不可重用。一旦latch达到0,就不能再使用了。可以用新的闩锁实例替换它,但这可能会产生竞争条件,除非做得正确。
【解决方案4】:

基于:

ManualResetEvent 允许线程通过以下方式相互通信 发信号。通常,这 沟通涉及一项任务 一个线程必须先于其他线程完成 线程可以继续。

从这里:

http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx

您可能想查看 Java 并发包中的障碍 - 特别是 CyclicBarrier 我相信:

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/CyclicBarrier.html

它会阻塞固定数量的线程,直到发生特定事件。所有线程必须在一个障碍点聚集在一起。

【讨论】:

  • 你能提供一些关于这个障碍的例子吗?我没有从文档中得到示例。
  • 哦,我找到了一篇文章:tutorials.jenkov.com/java-util-concurrent/cyclicbarrier.html 所以它会阻塞,直到所有线程都到达屏障(因此形成临界质量),然后它会继续。很有趣。现在我会知道这件事的。不幸的是,它对问题没有那么有用,但是它来了,每个java对象都有等待,shich是ManualResetEvent
  • @BogdanMart 但是它会自动重新阻止,这意味着它既不是ManualResetEvent也不是AutoResetEvent
  • @BogdanMart 我不是在谈论其他答案。我说的是你的评论是不真实的,并且误导了其他读者。
【解决方案5】:

我相信 .NET MRE 的关键是线程亲和性以及它在调用 Set 时让所有等待线程通过的能力。我发现信号量的使用效果很好。但是,如果我有 10 或 15 个线程在等待,那么我会遇到另一个问题。具体来说,它发生在调用 Set 时。在 .Net 中,所有等待的线程都被释放。使用信号量并不会全部释放。所以我把它包在一个班级里。注意:我非常熟悉 .NET 线程。我对 Java 线程和同步比较陌生。尽管如此,我还是愿意加入并获得一些真实的反馈。这是我的实现,假设 Java 新手会做出:

public class ManualEvent {
private final static int MAX_WAIT = 1000;
private final static String TAG = "ManualEvent"; 
private Semaphore semaphore = new Semaphore(MAX_WAIT, false);

private volatile boolean signaled = false;
public ManualEvent(boolean signaled) {
    this.signaled = signaled; 
    if (!signaled) {
        semaphore.drainPermits();
    }
}

public boolean WaitOne() {
    return WaitOne(Long.MAX_VALUE);
}

private volatile int count = 0;
public boolean WaitOne(long millis) {
    boolean bRc = true;
    if (signaled)
        return true;

    try {
        ++count;
        if (count > MAX_WAIT) {
            Log.w(TAG, "More requests than waits: " + String.valueOf(count));
        }

        Log.d(TAG, "ManualEvent WaitOne Entered");
        bRc = semaphore.tryAcquire(millis, TimeUnit.MILLISECONDS);
        Log.d(TAG, "ManualEvent WaitOne=" + String.valueOf(bRc));
    }
    catch (InterruptedException e) {
        bRc = false;
    }
    finally {
        --count;
    }

    Log.d(TAG, "ManualEvent WaitOne Exit");
    return bRc;
}

public void Set() {
    Log.d(TAG, "ManualEvent Set");
    signaled = true;
    semaphore.release(MAX_WAIT);
}

public void Reset() {
    signaled = false;
    //stop any new requests
    int count = semaphore.drainPermits();
    Log.d(TAG, "ManualEvent Reset: Permits drained=" + String.valueOf(count));
}

}

另外请注意,我基本上打赌在任何给定时间等待发布的请求都不超过 1000 个。通过批量释放和获取,我试图释放所有等待的线程。请注意,对 WaitOne 的调用一次只能处理 1 个许可。

【讨论】:

    猜你喜欢
    • 2010-11-29
    • 2012-08-03
    • 1970-01-01
    • 2018-08-26
    • 2014-02-26
    • 1970-01-01
    • 2011-12-26
    • 2015-06-13
    • 1970-01-01
    相关资源
    最近更新 更多