深入理解可重入锁

一、可重入锁概念

锁的概念就不用多解释了,当某个线程A已经持有了一个锁,当线程B尝试进入被这个锁保护的代码段的时候.就会被阻塞.

可重入 就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。

而锁的操作粒度是”线程”,而不是调用(至于为什么要这样,下面解释).同一个线程再次进入同步代码的时候.可以使用自己已经获取到的锁,这就是可重入锁。

java里面内置锁(synchronize)和Lock(ReentrantLock)都是可重入。

二、可重入背景介绍(为什么要可重入)

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

public class Widget {

        public synchronized void doSomething() {

            ...

        }

}

    public class LoggingWidget extends Widget {

        public synchronized void doSomething() {

            System.out.println(toString() + ": calling doSomething");

            super.doSomething();//若内置锁是不可重入的,则发生死锁        }

}

这个例子是java并发编程实战中的例子.synchronized 是父类Widget的内置锁,当执行子 类的方法的时候,先获取了一次Widget的锁,然后在执行super的时候,就要获取一次,如果不可重入,那么就跪了.

三、可重入锁的使用场景

可重入锁主要用在线程需要多次进入临界区代码时,需要使用可重入锁。具体的例子,比如上文中提到的一个synchronized方法需要调用另一个synchronized方法时。

为每个锁关联一个获取计数器和一个所有者线程,当计数值为0的时候,这个锁就没有被任何线程持有。

当线程请求一个未被持有的锁时,JVM将记下锁的持有者,并且将获取计数值置为1,如果同一个线程再次获取这个锁,计数值将递增,退出一次同步代码块,计算值递减,当计数值为0时,这个锁就被释放。

ps:可重入是指对同一线程而言。

public class ReentrantTest implements Runnable {

public synchronized void get() {

System.out.println(Thread.currentThread().getName());

set();

}

public synchronized void set() {

System.out.println(Thread.currentThread().getName());

}

public void run() {

get();

}

public static void main(String[] args) {

ReentrantTest rt =new ReentrantTest();

for(;;){

new Thread(rt).start();

}

}

}

四、可重入锁的实现原理

加锁时,需要判断锁是否已经被获取。如果已经被获取,则判断获取锁的线程是否是当前线程。如果是当前线程,则给获取次数加1。如果不是当前线程,则需要等待。

释放锁时,需要给锁的获取次数减1,然后判断,次数是否为0了。如果次数为0了,则需要调用锁的唤醒方法,让锁上阻塞的其他线程得到执行的机会。

你可能感兴趣的:(深入理解可重入锁)