可重入锁

关于可重入锁的一些基本知识~

1. 什么是可重入锁

锁的概念就不用多解释了,当某个线程A已经持有了一个锁,当线程B尝试进入被这个锁保护的代码段的时候,就会被阻塞。而锁的操作粒度是”线程”,而不是调用。同一个线程再次进入同步代码的时候,可以使用自己已经获取到的锁,这就是可重入锁。
Java里面内置锁(synchronize)和Lock(ReentrantLock)都是可重入的。

2. 为什么要可重入

如果线程A继续再次获得这个锁呢?比如一个方法是synchronized,递归调用自己,那么第一次已经获得了锁,第二次调用的时候还能进入吗?直观上当然需要能进入,这就要求必须是可重入,.可重入锁又叫做递归锁,下面是Java并发编程一书中的例子。

1
2
3
4
5
6
7
8
9
10
11
12
public 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
    16
    public 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
    25
    import 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();
    }
    }

5. 参考文章

0%