简体   繁体   English

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

[英]Reentrantlock - Why do we need to acquire a lock multiple times?

I have recently been learning multithreading concepts in java. 我最近一直在学习Java中的多线程概念。 I have a few doubts which didn't resolve by looking up the relevant threads on StackOverflow. 我有一些疑问,无法通过在StackOverflow上查找相关线程来解决。 I couldn't find satisfactory answers for my following questions: 对于以下问题,我找不到令人满意的答案:

  1. wait() method makes the thread to wait till it gets the lock. wait()方法使线程等待直到获得锁。 Whereas, the wait(long timeout) method makes the thread wait for 'timeout' no. 而wait(long timeout)方法使线程等待'timeout'否。 of milliseconds and if it still doesn't get the lock, goes back to runnable state. 毫秒,如果仍然无法获得锁定,则返回可运行状态。 But to actually get to running state, it needs the lock however. 但是要真正进入运行状态,它却需要锁。 So what's the point of wait(long timeout) method? 那么,wait(长超时)方法的意义何在? The thread however releases the locks acquired by it when it's in waiting state. 但是,线程在等待状态时会释放其获取的锁。 So the difference is not even the resources acquired by it. 因此,差异甚至不是它获得的资源。 What difference does it make if the thread stays in waiting state or runnable state? 如果线程保持等待状态或可运行状态,会有什么区别? What's the advantage of wait(long timeout) over wait() method? 与wait()方法相比,wait(long timeout)有什么优势?

  2. synchonized keyword or block provides lock on the object on which the method or block is called. synchonized关键字或块可在调用该方法或块的对象上提供锁定。 It causes another thread which tries to acquire the lock on the same instance to wait. 它导致另一个试图获取同一实例上的锁的线程等待。 But in the case of ReentrantLock, on which object is the lock acquired? 但是对于ReentrantLock,该锁是在哪个对象上获得的? The threads trying to acquire whose lock are made to wait? 试图获取其锁的线程正在等待?

  3. How does a ReentrantLock avoid deadlock? ReentrantLock如何避免死锁? Suppose there are two methods m1 and m2. 假设有两种方法m1和m2。 Both need to acquire a lock. 两者都需要获得一个锁。 m1 is calling m2 and m2 is calling m1. m1正在呼叫m2,m2正在呼叫m1。 How can we avoid deadlock in this situation using ReentrantLock? 在这种情况下,如何使用ReentrantLock避免死锁? May be we can use tryLock() and provide an alternate operations for the thread which fails to acquire the lock. 也许我们可以使用tryLock()并为无法获取锁的线程提供替代操作。 But what could be the possible alternate operations? 但是可能的替代操作是什么? What if the thread must need the lock to work? 如果线程必须需要锁才能工作怎么办?

  4. I found that using ReentrantLock we can acquire lock multiple times. 我发现使用ReentrantLock可以多次获取锁。 But why do we have to acquire lock several times? 但是为什么我们必须多次获得锁? I have read theoretical answers on this but couldn't really get it. 我已经阅读了有关此问题的理论答案,但无法真正理解。 It will be helpful if you can demonstrate with a clear example code. 如果您可以使用清晰的示例代码进行演示,将很有帮助。

Why do we need to acquire a lock multiple times? 为什么我们需要多次获取锁?

Clearly, you don't need to. 显然,您不需要 But it is not unusual for an application to do it "by accident". 但是,应用程序“偶然”执行此操作并不罕见。 For example: 例如:

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

In this example, the op object will actually get locked twice. 在此示例中, op对象实际上将被锁定两次。 If primitive locks weren't re-entrant, this could would fail (or deadlock). 如果不重入原始锁,则可能会失败(或死锁)。

Now we could fix the binaryOperation method to not do that, but it would make the code significantly more complicated. 现在我们可以修复binaryOperation方法不这样做,但这会使代码复杂得多。

The same scenario can happen with ReentrantLock . ReentrantLock可能发生相同的情况。


Question 1. 问题1

But to actually get to running state, it needs the lock however. 但是要真正进入运行状态,它却需要锁。 So what's the point of wait(long timeout) method? 那么,wait(长超时)方法的意义何在?

This is about Object::wait . 这是关于Object::wait The ReentrantLock API doesn't support this. ReentrantLock API不支持此功能。 (Beware: you can use wait and notify on a ReentrantLock object, but only if you are treating it as a primitive lock. Not a good idea!) (当心:您可以使用wait并在ReentrantLock对象上进行notify ,但ReentrantLock是将其视为原始锁。这不是一个好主意!)

The wait is waiting for a notification, and the timeout says how long the caller is prepared to wait for the notification. wait正在等待通知, timeout表示呼叫者准备等待通知的时间。 As the javadoc says: 正如javadoc所说:

"Causes the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed." “导致当前线程等待,直到另一个线程为此对象调用notify()方法或notifyAll()方法,或者经过了指定的时间。”

With both wait() and wait(timeout) , it is up to the caller to check that the condition that it was expecting to be "notified about" has actually satisfied. 使用wait()wait(timeout) ,由调用者检查是否确实满足了预期要“通知”的条件。 (See the note about "spurious wakeup" ... and the example code.) (请参阅有关“虚假唤醒”的注释以及示例代码。)

What's the advantage of wait(long timeout) over wait() method? 与wait()方法相比,wait(long timeout)有什么优势?

Simply, it gives you the option of only waiting a limited time for the notification. 简而言之,它使您可以选择仅在有限的时间内等待通知。 If this is not useful, don't use it. 如果这没有用,请不要使用它。


Question 2. 问题2。

But in the case of ReentrantLock , on which object is the lock acquired? 但是对于ReentrantLock ,在哪个对象上获得了锁?

Strictly speaking, it is the lock itself. 严格来说,它是锁本身。 What that the lock actually means will depend on how you code your classes. 锁的实际含义取决于您编写类的方式。 But that is exactly the same as with primitive mutexes. 但这与原始互斥锁完全相同。

Locking in Java doesn't some misbehaving code from accessing and or updating some shared state without holding the lock. 锁定Java不会在不持有锁定的情况下访问和/或更新某些共享状态而导致某些行为异常。 It is up to the programmer to do it correctly. 程序员必须正确地执行此操作。

The threads trying to acquire whose lock are made to wait? 试图获取其锁的线程正在等待?

Yes. 是。


Question 3. 问题3。

How does a ReentrantLock avoid deadlock? ReentrantLock如何避免死锁?

In general, it doesn't. 通常情况并非如此。

In the case of reentrant locking (ie in the case where a thread attempts to acquire lock A while holding lock A), the ReentrantLock implementation notices that the thread holding the lock is the thread acquiring the lock. 在可重入锁定的情况下(即,在线程试图持有锁A的同时获取锁A的情况下), ReentrantLock实现会注意到持有该锁的线程是获取该锁的线程。 A counter is incremented, so that the implementation knows that the lock must be released twice . 计数器增加,因此实现知道必须释放两次锁。

How can we avoid deadlock in this situation using ReentrantLock? 在这种情况下,如何使用ReentrantLock避免死锁? May be we can use tryLock() and provide an alternate operations for the thread which fails to acquire the lock. 也许我们可以使用tryLock()并为无法获取锁的线程提供替代操作。

That is one approach. 那是一种方法。

But what could be the possible alternate operations? 但是可能的替代操作是什么?

  1. Ensure that all threads acquire the locks in the same order. 确保所有线程以相同顺序获取锁。 (Deadlocks occur when threads attempt to acquire two or more threads in a different order.) (当线程尝试以不同顺序获取两个或多个线程时,将发生死锁。)

  2. If tryLock fails while holding a different lock, release the lock, wait a bit, and try again. 如果在持有其他锁的情况下tryLock失败,请释放该锁,稍等片刻,然后重试。

What if the thread must need the lock to work? 如果线程必须需要锁才能工作怎么办?

Then you design the logic so that deadlock is avoided; 然后设计逻辑,避免死锁。 see the alternatives above! 见上面的替代品!


Question 4. 问题4。

But why do we have to acquire lock several times? 但是为什么我们必须多次获得锁?

As stated above, you typically don't. 如上所述,您通常不需要。 But the point of a ReentrantLock is that you don't have to worry in the case where you do end up acquiring the lock twice ... for whatever reason. 但是ReentrantLock的要点是,您不必担心由于某种原因而最终两次获得该锁的情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Java的ReentrantLock究竟能获得什么锁定? - What exactly does Java's ReentrantLock acquire a lock on? 为什么私人的锁不能锁? - Why can't we acquire lock when it's private? 为什么我们需要为同步语句指定锁? - Why do we need to specify the lock for synchronized statements? 为什么我们需要锁定 Hashtable/ConcurrentHashMap 的 put 方法? - Why do we need lock in the put methods of Hashtable/ConcurrentHashMap? 为什么递归获取(可重入)锁? - Why acquire a (reentrant) lock recursively? 为什么要在释放操作中获取锁 - Why acquire a lock in a release operation 为什么我们需要Redis中的分布式锁 - Why we need distributed lock in the Redis ReentrantLock.tryLock(长时间超时,TimeUnit单位)在无法获取锁定时不超时 - ReentrantLock.tryLock(long timeout, TimeUnit unit) does not timeout when can not acquire lock 为什么不能在 ReentrantLock 的 object 上直接调用 await 和 signal 方法。 为什么我需要条件? - Why can't await and signal methods be called directly on object of ReentrantLock. Why do I need Condition? 为什么concurrentHashMap需要一个Segment数组? 为什么它无法在节点上获得锁定? - Why does concurrentHashMap need an array of Segment? Why cant it acquire lock on node?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM