java中的“重入”

时间:2013-05-12 04:34:03

标签: java reentrancy

重入意味着在每个线程而不是每次调用的基础上获取锁。

由于线程持有内部锁,这是否意味着线程运行一次等于调用基础?

谢谢,这似乎意味着:在一个线程中,如果我在调用函数lockA的进程函数doA中获得锁doB,并且doB也需要锁定lockA,然后就会有一个重入。在Java中,每个线程都会获得这种现象,所以我不需要考虑死锁吗?

6 个答案:

答案 0 :(得分:57)

  

重入意味着在每个线程而不是每次调用的基础上获取锁。

这是一个误导性的定义。这是真的(有点),但它错过了真正的观点。

Reentrancy意味着(通常是CS / IT术语)你做了什么,当你还在做的时候,你再做一次。在锁定的情况下,它意味着您在单个线程上执行类似的操作

  1. 获取对“foo”的锁定。
  2. 做点什么
  3. 获取“foo”的锁定。请注意,我们尚未发布之前获得的锁定。
  4. ...
  5. 释放锁定“foo”
  6. ...
  7. 释放锁定“foo”
  8. 使用可重入锁定/锁定机制,获取相同锁定的尝试将成功,并将增加属于该锁定的内部计数器。只有当锁的当前支架已经释放两次时才会释放锁。

    这是Java中使用原始对象锁/监视器的一个例子......它们是可重入的:

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

    reentrant的替代方法是非重入锁定,线程尝试获取已经存在的锁定时会出错。

    使用重入锁的优点是您不必担心因意外获取已经锁定的锁而导致失败的可能性。缺点是您无法假设您调用的任何内容都不会更改锁定旨在保护的变量的状态。但是,这通常不是问题。锁通常用于防止其他线程发生的并发状态更改。


      

    所以我不需要考虑死锁?

    是的,你做了。

    线程不会死锁(如果锁是可重入的)。但是,如果有其他线程可能锁定您要锁定的对象,则可能会出现死锁。

答案 1 :(得分:16)

想象一下这样的事情:

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

function B():
    A()

现在我们打电话给A.以下情况发生:

  • 我们输入A,锁定X
  • 我们输入B
  • 我们再次输入A,再次锁定X

由于我们从未退出A的第一次调用,因此X仍然被锁定。这称为重入 - 当函数A尚未返回时,再次调用函数A.如果A依赖于某种全局静态状态,则可能导致重新进入错误,在从函数退出中清除静态状态之前,该函数再次运行,并且半计算值与第二次调用的开始冲突。

在这种情况下,我们遇到了一个我们已经持有的锁。如果锁是重新进入的意识,它将意识到我们是同一个持有锁的线程并让我们通过。否则,它将永远死锁 - 它将等待它已经存在的锁定。

在java中,locksynchronized可以重新进入 - 如果线程持有锁,并且线程尝试重新获取相同的锁,则允许。因此,如果我们用Java编写上述伪代码,它就不会死锁。

答案 2 :(得分:9)

实践中的Java并发书状态 - Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.

让我解释它究竟意味着什么。首先,内在锁本质上是可重入的。实现重入的方式是通过维护所获得的锁的数量和锁的所有者的计数器。如果计数为0且没有所有者与之关联,则表示任何线程都不保留锁定。当线程获取锁时,JVM记录所有者并将计数器设置为1.如果同一线程再次尝试获取锁,则计数器递增,并且当拥有线程存在时,同步块计数器递减。当计数达到0时,锁定被释放。

一个简单的例子是 -

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

public class CustomTest extends Test {
    public synchronized void performTest() {
       //...
       super.performTest();
    }
}
没有重入就会出现僵局。

enter image description here

答案 3 :(得分:2)

  

重入意味着锁定是在每个线程而不是每次调用的基础上获得的。

让我用一个例子解释一下。

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

}

输出是:

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

与上面的代码一样,ReentrantTester包含两个同步的方法:methodA()和methodB() 第一个同步方法methodA()调用另一个同步方法methodB()。

当执行进入methodA()时,当前线程获取ReentrantTester对象的监视器。 现在,当methodA()调用methodB()时,由于methodB()也已同步,因此线程尝试获取 再次使用同一台显示器。因为Java支持可重入监视器,所以可以使用。当前线程获取ReentrantTester的 再次监视并继续执行methodA()和methodB()。

Java运行时允许线程重新获取它已经拥有的监视器,因为Java监视器是 重入。这些可重入监视器非常重要,因为它们消除了单线程的可能性 在已经拥有的监视器上陷入僵局。

答案 4 :(得分:1)

这只是意味着一旦线程有锁,它就可以根据需要多次输入锁定的代码段。因此,如果您有一个代码的同步部分,例如方法,只有获得锁定的线程可以调用该方法,但可以根据需要多次调用该方法,包括同一个锁持有的任何其他代码。如果您有一个方法调用另一个方法,并且两个方法都由同一个锁同步,这一点很重要。如果不是这样的话。第二个方法调用会阻塞。它也适用于递归方法调用。

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

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

}

答案 5 :(得分:0)

这与递归有关,请考虑以下问题:

{
  "name": "euc",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "~8.2.14",
    "@angular/cdk": "~8.2.3",
    "@angular/common": "~8.2.14",
    "@angular/compiler": "~8.2.14",
    "@angular/core": "~8.2.14",
    "@angular/forms": "~8.2.14",
    "@angular/material": "^8.2.3",
    "@angular/platform-browser": "~8.2.14",
    "@angular/platform-browser-dynamic": "~8.2.14",
    "@angular/router": "~8.2.14",
    "bootstrap": "^4.4.1",
    "hammerjs": "^2.0.8",
    "rxjs": "~6.4.0",
    "tslib": "^1.10.0",
    "zone.js": "~0.9.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.803.23",
    "@angular/cli": "~8.3.23",
    "@angular/compiler-cli": "~8.2.14",
    "@angular/language-service": "~8.2.14",
    "@types/node": "~8.9.4",
    "@types/jasmine": "~3.3.8",
    "@types/jasminewd2": "~2.0.3",
    "codelyzer": "^5.0.0",
    "jasmine-core": "~3.4.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.1.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "~2.0.1",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.4.0",
    "protractor": "~5.4.0",
    "ts-node": "~7.0.0",
    "tslint": "~5.15.0",
    "typescript": "~3.5.3"
  }
}

如果无法重新输入该锁,则线程可能会自身阻塞。