一、什么是可重入锁
可重入锁就是可以重复进入的锁,也叫递归锁。前提是同一把锁,如同一个类、同一个实例、同一个代码块。
来自知乎的解释:可重入锁指的是同一个线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。也就是说,线程可以进入任何一个他已经拥有锁的所有同步代码块。
Coffey强的解释:可重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。
synchronized 和 ReentrantLock 都是可重入锁。
可重入锁的意义之一在于防止死锁。synchronized 和 ReentrantLock 都是可重入锁。
二、代码演示
1、synchronized
public static void main(String[] args) { Object obj = new Object(); new Thread(() -> { //第一次加锁 synchronized (obj) { System.out.println(Thread.currentThread().getName() + "第一层"); //第二次加锁,此时obj对象处于锁定状态,但是当前线程仍然可以进入,避免死锁 synchronized (obj) { int a = 3 / 0; System.out.println(Thread.currentThread().getName() + "第二层"); } } }, "t1").start(); new Thread(() -> { //第一次加锁 synchronized (obj) { System.out.println(Thread.currentThread().getName() + "第一层"); //第二次加锁,此时obj对象处于锁定状态,但是当前线程仍然可以进入,避免死锁 synchronized (obj) { System.out.println(Thread.currentThread().getName() + "第二层"); } } }, "t2").start(); }
t1线程第二层有异常,使用synchronized关键字,发生异常,会自动释放锁,t2线程正常输出。
2、lock
public static void main(String[] args) { Lock lock = new ReentrantLock(false); new Thread(() -> { //第一次加锁 lock.lock(); try { System.out.println(Thread.currentThread().getName() + "第一层"); //第二次加锁,此时lock处于锁定状态,但是当前线程仍然可以进入,避免死锁 lock.lock(); try { int a = 3 / 0; System.out.println(Thread.currentThread().getName() + "第二层"); } finally { lock.unlock(); } } finally { lock.unlock(); } }, "t1").start(); new Thread(() -> { //第一次加锁 lock.lock(); try { System.out.println(Thread.currentThread().getName() + "第一层"); //第二次加锁,此时lock处于锁定状态,但是当前线程仍然可以进入,避免死锁 lock.lock(); try { //int a=3/0; System.out.println(Thread.currentThread().getName() + "第二层"); } finally { lock.unlock(); } } finally { lock.unlock(); } }, "t2").start(); }
t1线程第二层有异常,我们的释放锁写在finally里边,不影响t2线程正常输出。
源码推荐的写法
三、Lock和Synchronized的异同