【问题标题】:Is there a Mutex in Java?Java中有互斥锁吗?
【发布时间】:2011-07-14 13:05:17
【问题描述】:

在 java 中是否有 Mutex 对象或创建方法? 我问是因为用 1 个许可初始化的信号量对象对我没有帮助。 想想这个案例:

try {
   semaphore.acquire();
   //do stuff
   semaphore.release();
} catch (Exception e) {
   semaphore.release();
}

如果在第一次获取时发生异常,catch 块中的释放将增加许可,信号量不再是二进制信号量。

正确的方法是什么?

try {
   semaphore.acquire();
   //do stuff
} catch (Exception e) {
   //exception stuff
} finally {
   semaphore.release();
}

上面的代码会保证信号量是二进制的吗?

【问题讨论】:

  • 查看 java.util.concurrent.locks.AbstractQueuedSynchronizer 的 javadoc。它有一个如何编写 Mutex 类的示例。 -dbednar
  • 您是否凭经验发现了这种行为?实现是否在 1-permit Semaphore 上执行 release() 会添加额外的许可,即使它当前持有另一个许可,真的吗?

标签: java mutex semaphore java.util.concurrent concurrent-programming


【解决方案1】:

Java 中的任何对象都可以通过synchronized 块用作锁。这也会在发生异常时自动处理释放锁。

Object someObject = ...;

synchronized (someObject) {
  ...
}

您可以在此处阅读更多信息:Intrinsic Locks and Synchronization

【讨论】:

  • 非常有帮助的购买我想使用信号量。
  • @Noam:只需将代码与信号量和synchronized 进行比较,您就会发现什么更易读且不易出错。
  • 如果您希望以不同的方式释放锁(例如transaction.begin(); transaction.commit()),则不能使用同步关键字。
  • 它不是面向对象的..它的大部分低级同步
  • 在查看此答案的代码时,还请查看 someObject.wait(timeout)someObject.notify()
【解决方案2】:

查看此页面:http://www.oracle.com/technetwork/articles/javase/index-140767.html

它有一个稍微不同的模式,这是(我认为)你正在寻找的:

try {
  mutex.acquire();
  try {
    // do something
  } finally {
    mutex.release();
  }
} catch(InterruptedException ie) {
  // ...
}

在这种用法中,您只是在成功的acquire() 之后调用release()

【讨论】:

  • 你是如何定义互斥变量的
【解决方案3】:

没有人明确提到这一点,但这种模式通常不适合信号量。原因是任何线程都可以释放信号量,但你通常只想要originally locked to be able to unlock所有者线程。对于这个用例,在 Java 中,我们通常使用 ReentrantLocks,可以这样创建:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

private final Lock lock = new ReentrantLock(true);

而通常的设计使用模式是:

  lock.lock();
  try {
      // do something
  } catch (Exception e) {
      // handle the exception
  } finally {
      lock.unlock();
  }

Here 是 java 源代码中的一个示例,您可以在其中看到此模式的运行情况。

可重入锁具有支持公平性的额外好处。

仅当您需要非所有权释放语义时才使用信号量。

【讨论】:

  • 其实这应该是这个问题的(唯一)正确答案。清晰解释信号量和互斥锁的区别。将信号量与count=1 一起使用不是互斥锁。
  • 很高兴有人指出。对于资源互斥锁的独占访问是要走的路。二进制信号量不是互斥体,信号量应该更多地用作信号机制。
  • Rouble:lock 也是如此,例如ReentrantLockmutex?我不确定为什么 mutexbinary semaphore 被视为同一个实体。 Semaphore 可以被任何线程释放,因此不能保证保护critical section。有什么想法吗?
  • @Kaihua:我对你的想法产生了共鸣。这个答案带来了关键的区别
【解决方案4】:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


private final Lock _mutex = new ReentrantLock(true);

_mutex.lock();

// your protected code here

_mutex.unlock();

【讨论】:

  • 这在哪些方面优于已经提供的解决方案?它如何解决原始提问者遇到的问题?
  • @Martin: "Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements.", from: docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/… ...虽然你说得有道理。 Argv 的回答没有说​​明或解释这些操作。
  • 这是一个递归互斥锁,允许从同一个线程进行多次重新锁定,这可能会出现问题。 “真实”的基本互斥锁(非递归,C++ 风格)一次只允许一个锁。如果您将您的行更改为private final ReentrantLock _mutex = ...,则可以使用getHoldCount() 返回线程重新锁定的次数。 (您可以应用Condition 来防止这种情况。See the API。)
  • 所以互斥锁只是一个锁
【解决方案5】:

我认为你应该尝试:

信号量初始化时:

Semaphore semaphore = new Semaphore(1, true);

在你的Runnable Implementation

try 
{
   semaphore.acquire(1);
   // do stuff

} 
catch (Exception e) 
{
// Logging
}
finally
{
   semaphore.release(1);
}

【讨论】:

  • 我就是这样做的,但我不太确定这是否是要走的路。
  • 根据docs.oracle.com/javase/7/docs/api/java/util/concurrent/…“没有要求释放许可的线程必须通过调用acquire获得该许可。信号量的正确使用由应用程序中的编程约定建立。”如果acquire抛出异常,finally中的release会错误的释放permit。此线程中的其他示例显示了正确的流程。
【解决方案6】:

原始帖子中的错误是在 try 循环中设置了 acquire() 调用。 这是使用“二进制”信号量 (Mutex) 的正确方法:

semaphore.acquire();
try {
   //do stuff
} catch (Exception e) {
   //exception stuff
} finally {
   semaphore.release();
}

【讨论】:

    【解决方案7】:

    每个对象的锁都与 Mutex/Semaphore 设计略有不同。 例如,没有办法通过释放前一个节点的锁并捕获下一个节点来正确实现遍历链接节点。但是使用互斥锁很容易实现:

    Node p = getHead();
    if (p == null || x == null) return false;
    p.lock.acquire();  // Prime loop by acquiring first lock.
    // If above acquire fails due to interrupt, the method will
    //   throw InterruptedException now, so there is no need for
    //   further cleanup.
    for (;;) {
    Node nextp = null;
    boolean found;
    try { 
     found = x.equals(p.item); 
     if (!found) { 
       nextp = p.next; 
       if (nextp != null) { 
         try {      // Acquire next lock 
                    //   while still holding current 
           nextp.lock.acquire(); 
         } 
         catch (InterruptedException ie) { 
          throw ie;    // Note that finally clause will 
                       //   execute before the throw 
         } 
       } 
     } 
    }finally {     // release old lock regardless of outcome 
       p.lock.release();
    } 
    

    目前java.util.concurrent 中没有这样的类,但是你可以在Mutex.java 找到 Mutext 实现。至于标准库,Semaphore 提供了所有这些功能以及更多功能。

    【讨论】:

      【解决方案8】:

      要确保Semaphore 是二进制的,您只需要确保在创建信号量时将许可数传递为1。 Javadocs 有更多解释。

      【讨论】:

      • 不抱歉。它可以用作二进制信号量,它不是二进制信号量。如果您在其上调用许多 release(),则允许释放许多获取线程
      猜你喜欢
      • 2018-05-23
      • 1970-01-01
      • 2012-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-04
      相关资源
      最近更新 更多