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


wait()方法使线程等待直到获得锁。而wait(long timeout)方法使线程等待'timeout'否。毫秒,如果仍然无法获得锁定,则返回可运行状态。但是要真正进入运行状态,它却需要锁。那么,wait(长超时)方法的意义何在?但是,线程在等待状态时会释放其获取的锁。因此,差异甚至不是它获得的资源。如果线程保持等待状态或可运行状态,会有什么区别?与wait()方法相比,wait(long timeout)有什么优势?
synchonized关键字或块可在调用该方法或块的对象上提供锁定。它导致另一个试图获取同一实例上的锁的线程等待。但是对于ReentrantLock,锁是在哪个对象上获得的?试图获取其锁定的线程正在等待?
ReentrantLock如何避免死锁?假设有两种方法m1和m2。两者都需要获得一个锁。 m1正在呼叫m2,而m2正在呼叫m1。在这种情况下,如何使用ReentrantLock避免死锁?也许我们可以使用tryLock()并为无法获取锁的线程提供替代操作。但是可能的替代操作是什么?如果线程必须需要锁才能工作怎么办?
我发现使用ReentrantLock可以多次获取锁。但是为什么我们必须多次获得锁?我已经阅读了有关此问题的理论答案,但无法真正理解。如果您可以使用清晰的示例代码进行演示,将很有帮助。

最佳答案

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


显然,您不需要。但是,应用程序“偶然”执行此操作并不罕见。例如:

  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


  但是要真正进入运行状态,它却需要锁。那么,wait(长超时)方法的意义何在?


这是关于Object::waitReentrantLock API不支持此功能。 (请注意:您可以在wait对象上使用notifyReentrantLock,但是仅当您将其视为原始锁时才行。这不是一个好主意!)

wait正在等待通知,并且timeout表示呼叫者准备等待通知的时间。正如javadoc所说:


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


使用wait()wait(timeout),由调用者检查是否确实满足了预期要“通知”的条件。 (请参阅有关“虚假唤醒”的注释以及示例代码。)


  与wait()方法相比,wait(long timeout)有什么优势?


简而言之,它使您可以选择仅在有限的时间内等待通知。如果这没有用,请不要使用它。



问题2。


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


严格来说,它是锁本身。锁的实际含义取决于您编写类的方式。但这与原始互斥锁完全相同。

锁定Java不会在不持有锁定的情况下访问和/或更新某些共享状态而导致某些行为异常。程序员必须正确执行此操作。


  试图获取其锁定的线程正在等待?


是。



问题3。


  ReentrantLock如何避免死锁?


通常情况并非如此。

在可重入锁定的情况下(即,在线程尝试持有锁A的同时尝试获取锁A的情况),ReentrantLock实现会注意到持有锁的线程是获取锁的线程。计数器增加,因此实现知道该锁必须释放两次。


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


那是一种方法。


  但是可能的替代操作是什么?



确保所有线程以相同顺序获取锁。 (当线程尝试以不同的顺序获取两个或多个线程时,将发生死锁。)
如果tryLock在持有其他锁时失败,请释放该锁,稍等片刻,然后重试。



  如果线程必须需要锁才能工作怎么办?


然后设计逻辑,避免死锁。见上面的替代品!



问题4。


  但是为什么我们必须多次获得锁?


如上所述,您通常不需要。但是ReentrantLock的要点是,您不必担心由于某种原因而最终两次获得锁的情况。

关于java - Reentrantlock-为什么我们需要多次获取锁?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57440851/

10-10 02:42