当一个线程正在请求一个被其他线程持有的锁的时候,会被挂起。但是因为锁的可重入性,一个线程再次请求被自己持有的锁,这个请求会成功。锁的可重入性意味着锁的占有是基于线程的而不是基于调用次数的。
锁的可重入性通过一个锁的占用计数和当前占用线程来实现,当占用计数是零的时候,这个锁会被认为是一个未被占用的锁,当一个线程请求一个未占用的锁的时候,JVM会记录占用锁的当前线程,然后设置占用计数为1. 如果当前持有线程再次请求这个锁,那么计数器将会加1. 如果当前线程退出同步块,那么锁占用计数将会减1,如果最后的占用计数为零,那么锁将会被释放
锁的可重入性极大的简化了面向对象编程的代码,如果没有锁的可重入性,那么一些即使看起来很简单的代码也会出错。
public class Base {
public synchronized void method() {
}
}
public class Sub extends Base {
public synchronized void method() {
//do something
super.method();
}
}
在上面的代码中,如果没有锁的可重入性,当一个线程执行Sub的 method 方法时,首先会获取Sub当前实例的锁,当时在调用super的时候,同样需要当前实例的锁,但是这个时候第一次取得的锁并没有释放,所以这样便形成了死锁。