简体   繁体   English

Java中的“ReentrantLock”是什么意思?

[英]What is the meaning of "ReentrantLock" in Java?

Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.重入意味着锁是在每个线程而不是每个调用的基础上获取的。

Since an intrinsic lock is held by a thread, doesn't it mean that a thread run once equals an invocation basis?既然内在锁是由线程持有的,是不是就意味着一个线程运行一次就等于一个调用基础?

Thank you, it seems mean that: in a thread,if I get a lock lockA when process function doA which call function doB , and doB also need a lock lockA ,then there wil be a reentrancy.谢谢你,似乎意味着:在一个线程,如果我得到一个锁lockA当处理功能doA其通话功能doBdoB还需要一个锁lockA ,则有西港岛线是重入。 In Java, this phenomenon is acquired per thread, so I needn't consider deadlocks?在Java中,这种现象是每个线程都获得的,所以我不需要考虑死锁吗?

Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.重入意味着锁是在每个线程而不是每个调用的基础上获取的。

That is a misleading definition.这是一个误导性的定义。 It is true (sort of), but it misses the real point.这是真的(有点),但它没有抓住真正的重点。

Reentrancy means (in general CS / IT terminology) that you do something, and while you are still doing it, you do it again.重入意味着(在一般的 CS / IT 术语中)你做某事,当你还在做的时候,你又做了一次。 In the case of locks it means you do something like this on a single thread :在锁的情况下,这意味着您在单个线程上执行以下操作

  1. Acquire a lock on "foo".获得对“foo”的锁定。
  2. Do something做一点事
  3. Acquire a lock on "foo".获得对“foo”的锁定。 Note that we haven't released the lock that we previously acquired.请注意,我们还没有释放我们之前获得的锁。
  4. ... ...
  5. Release lock on "foo"释放对“foo”的锁定
  6. ... ...
  7. Release lock on "foo"释放对“foo”的锁定

With a reentrant lock / locking mechanism, the attempt to acquire the same lock will succeed, and will increment an internal counter belonging to the lock.使用可重入锁/锁定机制,尝试获取相同的锁将成功,并将增加属于该锁的内部计数器。 The lock will only be released when the current holder of the lock has released it twice.只有当锁的当前持有者释放它两次时,才会释放锁。

Here's a example in Java using primitive object locks / monitors ... which are reentrant:这是 Java 中使用原始对象锁/监视器的示例......它们是可重入的:

Object lock = new Object();
...
synchronized (lock) {
    ...
    doSomething(lock, ...)
    ...
}

public void doSomething(Object lock, ...) {
    synchronized (lock) {
        ...
    }
}

The alternative to reentrant is non-reentrant locking, where it would be an error for a thread to attempt to acquire a lock that it already holds.可重入的替代方法是不可重入锁定,在这种情况下,线程尝试获取它已经持有的锁将是错误的。

The advantage of using reentrant locks is that you don't have to worry about the possibility of failing due to accidentally acquiring a lock that you already hold.使用可重入锁的优点是您不必担心由于意外获取您已经持有的锁而失败的可能性。 The downside is that you can't assume that nothing you call will change the state of the variables that the lock is designed to protect.缺点是你不能假设你调用的任何东西都不会改变锁旨在保护的变量的状态。 However, that's not usually a problem.但是,这通常不是问题。 Locks are generally used to protect against concurrent state changes made by other threads.锁通常用于防止其他线程进行并发状态更改。


So I needn't consider deadlocks?所以我不需要考虑死锁?

Yes you do.是的你是。

A thread won't deadlock against itself (if the lock is reentrant).线程不会对自身造成死锁(如果锁是可重入的)。 However, you could get a deadlock if there are other threads that might have a lock on the object you are trying to lock.但是,如果有其他线程可能锁定了您要锁定的对象,则可能会出现死锁。

Imagine something like this:想象一下这样的事情:

function A():
   lock (X)
       B()
   unlock (X)

function B():
    A()

Now we call A. The following happens:现在我们称 A。 发生以下情况:

  • We enter A, locking X我们输入A,锁定X
  • We enter B我们输入B
  • We enter A again, locking X again我们再次输入 A,再次锁定 X

Since we never exited the first invocation of A, X is still locked.由于我们从未退出 A 的第一次调用,因此 X 仍然被锁定。 This is called re-entrance - while function A has not yet returned, function A is called again.这称为重入 - 当函数 A 尚未返回时,再次调用函数 A。 If A relies on some global, static state, this can cause a 're-entrance bug', where before the static state is cleaned up from the function's exit, the function is run again, and the half computed values collide with the start of the second call.如果 A 依赖于一些全局的静态状态,这可能会导致“重入错误”,在从函数的退出清除静态状态之前,该函数再次运行,并且一半的计算值与第二个电话。

In this case, we run into a lock we are already holding.在这种情况下,我们遇到了一个我们已经持有的锁。 If the lock is re-entrance aware, it will realize we are the same thread holding the lock already and let us through.如果锁是重入感知的,它将意识到我们已经是持有锁的同一个线程并让我们通过。 Otherwise, it will deadlock forever - it will be waiting for a lock it already holds.否则,它将永远死锁——它将等待一个它已经持有的锁。

In java, lock and synchronized are re-entrance aware - if a lock is held by a thread, and the thread tries to re-acquire the same lock, it is allowed.在java中, locksynchronized是重入感知的——如果一个锁被一个线程持有,并且该线程试图重新获取同一个锁,这是允许的。 So if we wrote the above pseudocode in Java, it would not deadlock.所以如果我们用Java写上面的伪代码,就不会死锁。

Java concurrency in practice book states - Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis. Java 并发实践手册指出 - Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.

Let me explain what it exactly means.让我解释一下它的确切含义。 First of all Intrinsic locks are reentrant by nature.首先,内在锁本质上是可重入的。 The way reentrancy is achieved is by maintaining a counter for number of locks acquired and owner of the lock.实现可重入的方法是维护获取的锁数量和锁所有者的计数器。 If the count is 0 and no owner is associated to it, means lock is not held by any thread.如果计数为 0 并且没有所有者与之关联,则意味着任何线程都没有持有锁。 When a thread acquires the lock, JVM records the owner and sets the counter to 1.If same thread tries to acquire the lock again, the counter is incremented.当线程获取锁时,JVM 记录所有者并将计数器设置为 1。如果同一线程再次尝试获取锁,则计数器递增。 And when the owning thread exits synchronized block, the counter is decremented.当拥有线程退出同步块时,计数器递减。 When count reaches 0 again, lock is released.当计数再次达到 0 时,锁被释放。

A simple example would be -一个简单的例子是——

public class Test {
    public synchronized void performTest() {
       //...
    }
}

public class CustomTest extends Test {
    public synchronized void performTest() {
       //...
       super.performTest();
    }
}

without reentrancy there would be a deadlock.如果没有可重入性,就会出现僵局。

在此处输入图片说明

Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.重入意味着锁是在每个线程而不是每个调用的基础上获取的。

Let me explain this with an example.让我用一个例子来解释这一点。

class ReentrantTester {

    public synchronized void methodA() {
      System.out.println("Now I am inside methodA()");
      methodB();
    }

    public synchronized void methodB() {
      System.out.println("Now I am inside methodB()");
    }

    public static void main(String [] args) {
        ReentrantTester rt = new ReentrantTester();
        rt.methodA();  
    }

}

The out put is :输出是:

Now I am inside methodA()
Now I am inside methodB()

As in the above code, the ReentrantTester contains two synchronized methods: methodA() & methodB() The first synchronized method methodA() calls the other synchronized method methodB().在上面的代码中,ReentrantTester 包含两个同步方法:methodA() & methodB() 第一个同步方法methodA()调用另一个同步方法methodB()。

When execution enters the methodA(), the current thread acquires the monitor for the ReentrantTester object.当执行进入methodA()时,当前线程获取ReentrantTester对象的监视器。 Now when methodA() calls methodB(), because methodB() is also synchronized, the thread attempts to acquire the same monitor again.现在当methodA()调用methodB()时,因为methodB()也是同步的,线程再次尝试获取同一个监视器。 Because Java supports reentrant monitors, this works.因为 Java 支持可重入监视器,所以这是有效的。 The current thread acquire the ReentrantTester's monitor again and continue the execution of both methodA() and methodB().当前线程再次获取 ReentrantTester 的监视器并继续执行 methodA() 和 methodB()。

The Java runtime allows a thread to reacquire a monitor that it already holds, because Java monitors are reentrant. Java 运行时允许线程重新获取它已经拥有的监视器,因为 Java 监视器是可重入的。 These reentrant monitors are important because they eliminate the possibility of a single thread deadlocking itself on a monitor that it already holds.这些可重入监视器很重要,因为它们消除了单个线程在它已经拥有的监视器上死锁的可能性。

This just means once a thread has a lock it may enter the locked section of code as many times as it needs to.这只是意味着一旦一个线程获得了锁,它就可以根据需要多次进入被锁定的代码段。 So if you have a synchronized section of code such as a method, only the thread which attained the lock can call that method, but can call that method as many times as it wants, including any other code held by the same lock.因此,如果您有代码的同步部分,例如方法,则只有获得锁的线程才能调用该方法,但可以根据需要多次调用该方法,包括由同一锁​​持有的任何其他代码。 This is important if you have one method that calls another method, and both are synchronized by the same lock.如果您有一个方法调用另一个方法,并且两者都由同一个锁同步,那么这一点很重要。 If this wasn't the case the.如果不是这种情况。 The second method call would block.第二个方法调用会阻塞。 It would also apply to recursive method calls.它也适用于递归方法调用。

public void methodA()
{
     // other code
     synchronized(this)
     {
          methodB();
     } 
}

public void methodB()
{
     // other code
     syncrhonized(this)
     {
          // it can still enter this code    
     }

}

it's about recurse, think about:这是关于递归的,请考虑:

private lock = new ReentrantLock();
public void method() {
      lock.lock();
      method();
}

If the lock is not re-entrant able, the thread could block itself.如果锁不可重入,则线程可能会阻塞自身。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM