首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Reentrantlock -为什么我们需要多次获得锁?

Reentrantlock -为什么我们需要多次获得锁?
EN

Stack Overflow用户
提问于 2019-08-10 09:15:21
回答 2查看 2.7K关注 0票数 3

最近,我一直在学习java中的多线程概念。我有一些疑问,没有通过查找StackOverflow上的相关线程来解决。以下问题我找不到满意的答案:

  1. 方法使线程等待直到获得锁。然而,等待(长超时)方法使线程等待‘超时值’否。如果它仍然没有得到锁,则返回可运行状态。但是要真正进入运行状态,它需要锁。那么等待(长超时)方法有什么意义呢?然而,当线程处于等待状态时,线程会释放它获得的锁。因此,区别甚至不是它所获得的资源。如果线程保持在等待状态或可运行状态,会有什么不同?与等待()方法相比,等待(长超时)方法的优点是什么?
  2. 同步关键字或块提供对调用方法或块的对象的锁定。它导致另一个线程试图获取同一个实例上的锁等待。但是在ReentrantLock的情况下,锁是在哪个对象上获得的?试图获取谁的锁的线程是用来等待的?
  3. ReentrantLock如何避免死锁?假设有两个方法m1和m2。两个人都需要锁。m1打电话给m2,m2打给m1。在这种情况下,如何使用ReentrantLock避免死锁?也许我们可以使用tryLock(),并为无法获取锁的线程提供一个备用操作。但可能的替代行动是什么呢?如果线程必须需要锁才能工作呢?
  4. 我发现使用ReentrantLock我们可以多次获得锁。但是为什么我们要多次获得锁呢?我读过关于这方面的理论答案,但没能真正理解。如果您可以用一个清晰的示例代码演示,这将是很有帮助的。
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-08-10 14:56:31

为什么我们需要多次获得锁?

很明显,你不需要。但是,申请“偶然”这样做并不罕见。例如:

代码语言:javascript
运行
复制
  public void binaryOperation(Operand op1, Operand op2) {
      synchronized (op1) {
          synchronized (op2) {
               // do something that needs the locks
          }
      }
  }

  // now call passing the same object for both operands
  Operand op = ...
  binaryOperation(op, op); 

在本例中,op对象实际上将被锁定两次。如果原始锁不是可重入的,这可能会失败(或者死锁)。

现在我们可以修复binaryOperation方法而不是这样做,但是它会使代码变得更加复杂。

同样的场景也可以发生在ReentrantLock上。

问题1.

但是要真正进入运行状态,它需要锁。那么等待(长超时)方法有什么意义呢?

这是关于Object::wait的。ReentrantLock API不支持这一点。(注意:您可以在一个wait对象上使用notifynotify,但前提是您将它作为一个原始锁处理。不是个好主意!)

wait正在等待通知,而timeout则表示调用方准备等待通知的时间。正如javadoc所说:

“导致当前线程等待,直到另一个线程为该对象调用notify()方法或notifyAll()方法,或经过指定的时间。”

对于wait()wait(timeout),应该由调用方来检查它希望得到“通知”的条件是否确实满足。(见关于“虚假唤醒”的注释.以及示例代码。)

与等待()方法相比,等待(长超时)方法的优点是什么?

简单地说,它为您提供了只等待通知的有限时间的选项。如果这没有用,就不要用它。

问题2.

但是在ReentrantLock的情况下,锁是在哪个对象上获得的?

严格地说,这是锁本身。锁的实际含义将取决于您如何编写类的代码。但这和原始互斥完全一样。

在Java中锁定并不能阻止一些行为不当的代码在不保持锁的情况下访问和更新某些共享状态。这取决于程序员是否正确地做了它。

试图获取谁的锁的线程是用来等待的?

是。

问题3.

ReentrantLock如何避免死锁?

一般情况下,事实并非如此。

在重入锁定的情况下(即当线程试图在持有锁A时获取锁A时),ReentrantLock实现注意到持有锁的线程是获取锁的线程。计数器被递增,以便实现知道锁必须释放两次。

在这种情况下,如何使用ReentrantLock避免死锁?也许我们可以使用tryLock(),并为无法获取锁的线程提供一个备用操作。

这是一种方法。

但可能的替代行动是什么呢?

  1. 确保所有线程都以相同的顺序获取锁。(当线程试图以不同的顺序获取两个或多个线程时,就会发生死锁。)
  2. 如果tryLock在持有另一个锁时失败,请释放锁,稍等,然后再试一次。

如果线程必须需要锁才能工作呢?

然后设计逻辑,以避免死锁;请参阅上面的备选方案!

问题4.

但是为什么我们要多次获得锁呢?

正如上面所述,您通常不需要。但是ReentrantLock的要点是,当您最终获得锁两次时,您不必担心.不管出于什么原因。

票数 4
EN

Stack Overflow用户

发布于 2022-10-30 12:29:04

我认为在某些Oracle文档中很少讨论可重入性的用例(为什么我们要多次获得锁):

ReentrantReadWriteLock类

此锁允许读取器和写入器以ReentrantLock的样式重新获取读或写锁。在所有由写入线程持有的写锁被释放之前,不能允许不可重入的读取器。 此外,作者可以获得读锁,但反之亦然。在其他应用程序中,在调用或回调在读锁下执行读取的方法时,可重入性可能非常有用。如果读取器试图获取写入锁,则永远不会成功。

重入同步

回想一下,一个线程不能获得另一个线程拥有的锁。但是线程可以获得它已经拥有的锁。允许线程多次获取相同的锁可启用重入同步。这描述了同步代码直接或间接调用也包含同步代码的方法的情况,而这两组代码使用相同的锁。如果没有重入同步,同步代码将不得不采取许多额外的预防措施,以避免线程导致自身阻塞.。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57440851

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档