关于可重入锁的一些基本知识~
1. 什么是可重入锁
锁的概念就不用多解释了,当某个线程A已经持有了一个锁,当线程B尝试进入被这个锁保护的代码段的时候,就会被阻塞。而锁的操作粒度是”线程”,而不是调用。同一个线程再次进入同步代码的时候,可以使用自己已经获取到的锁,这就是可重入锁。
Java里面内置锁(synchronize)和Lock(ReentrantLock)都是可重入的。
2. 为什么要可重入
如果线程A继续再次获得这个锁呢?比如一个方法是synchronized,递归调用自己,那么第一次已经获得了锁,第二次调用的时候还能进入吗?直观上当然需要能进入,这就要求必须是可重入,.可重入锁又叫做递归锁,下面是Java并发编程一书中的例子。1
2
3
4
5
6
7
8
9
10
11
12public class Widget {
public synchronized void doSomething() {
// do somethig here...
}
}
public class LoggingWidget extends Widget {
public synchronized void doSomething() {
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}
}
书中原文是这么写的,
“由于Widget和LoggingWidget中doSomething方法都是synchronized方法,因此每个doSomthing 方法在执行前都会获得Widget上的锁”
作者或者翻译这句话描述有误或者不够严谨,synchronized在对方法使用的时候相当于使用 synchronized(this),所以其实是用的该类实例的对象上的锁标志位,补上省略的主语和错误的地方后应该是这样:
“由于Widget和LoggingWidget中doSomething方法都是synchronized方法,因此线程在每个doSomthing方法在执行前都会获得LoggingWidget实例上的锁”
- 当线程执行LoggingWidget实例中的doSomething时获得LoggingWidget实例的锁。
- LoggingWidget实例doSomething方法中调用super.doSomething(),调用者依然是LoggingWidget实例,再次获得的锁依然是LoggingWidget实例的锁。
- 线程再次获得LoggingWidget实例的锁,即锁的重入。
3. 如何实现可重入锁
为每个锁关联一个获取计数器和一个所有者线程,当计数值为0的时候,这个所就没有被任何线程只有。当线程请求一个未被持有的锁时,JVM将记下锁的持有者,并且将获取计数值置为1,如果同一个线程再次获取这个锁,技术值将递增,退出一次同步代码块,计算值递减,当计数值为0时,这个锁就被释放.ReentrantLock里面有实现。
4. 两种可重入锁demo
syncronized
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class ReentrantTest {
public void method1() {
synchronized (ReentrantTest.class) {
System.out.println("方法1获得ReentrantTest的内置锁运行了");
method2();
}
}
public void method2() {
synchronized (ReentrantTest.class) {
System.out.println("方法1里面调用的方法2重入内置锁,也正常运行了");
}
}
public static void main(String[] args) {
new ReentrantTest().method1();
}
}ReentrantLock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTest {
private Lock lock = new ReentrantLock();
public void method1() {
lock.lock();
try {
System.out.println("方法1获得ReentrantLock锁运行了");
method2();
} finally {
lock.unlock();
}
}
public void method2() {
lock.lock();
try {
System.out.println("方法1里面调用的方法2重入ReentrantLock锁,也正常运行了");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
new ReentrantLockTest().method1();
}
}