首页 > 编程笔记 > Java笔记 阅读:13

Java synchronized可重入锁的底层实现(新手必看)

可重入锁也称为递归锁,在同一线程内,外层函数获得该锁之后,内层递归函数仍然可以获取到该锁。这种锁在同一线程内是安全的,因为它可以被同一线程多次获取,而不会产生不一致的状态。

可重入锁的意义之一在于防止死锁。一个线程在已经持有锁的情况下,再次请求该锁,如果锁是不可重入的,那么该线程在第二次请求锁时将被阻塞,因为它已经拥有了该锁。在这种情况下,该线程可能会因为无法获取该锁而导致程序发生死锁。可重入锁通过允许一个线程多次获取同一个锁,保证了线程的执行不会被阻塞,从而避免了死锁问题。

synchronized 是可重入锁吗?我们先看一个示例,具体代码如下:
public class SynchronizedLockTest {
    public static void main(String[] args) {
        new SynchronizedLock().m1();
    }
}
class SynchronizedLock {
    static Object lock = new Object();
    public void m1() {
        synchronized (lock) {
            System.out.println("m1外层执行!");
            m2();
        }
    }
    public void m2() {
       synchronized (lock) {
           System.out.println("m2中层执行!");
           m3();
       }
    }
    public void m3() {
        synchronized (lock) {
            System.out.println("m3内层执行!");
        }
    }
}
示例代码执行结果如下所示:

m1外层执行!
m2中层执行!
m3内层执行!

在上述代码中,m1、m2、m3 这 3 个方法都有 synchronized 代码块,当调用 m1() 方法时,会嵌套调用 m2()、m3() 方法。根据执行结果,我们发现 synchronized 嵌套调用未发生阻塞,顺利完成执行。在一个线程使用 synchronized 方法时调用该对象的另一个 synchronized 方法,即一个线程得到一个对象锁后再次请求该对象锁,因此我们可以确定 synchronized 是可重入锁。

在 Java 内部,当同一个线程调用自己类中其他 synchronized 方法或代码块时不会阻碍该线程的执行,同一个线程对同一个对象锁是可重入的,而且同一个线程可以获取同一把锁多次,即可以多次重入。

通过上面的详解我们知道了 synchronized 是可重入锁,那么可重入锁的实现原理是什么呢?

要知道,每个对象都有一个关联的 Monitor,Monitor 有一个计数器,当一个线程要进入 synchronized 代码块时,需要先判断 Monitor 的计数器的值是否为 0,如果该值为 0 表示该锁没有被任何线程持有,线程可以获得该锁并调用相应方法。

当一个线程请求成功后,Monitor 会记下持有锁的线程,并将计数器的值记为 1。此时其他线程如果请求该锁,则必须等待,而持有锁的线程如果再次请求这个锁,就可以再次获取,同时计数器会递增 1。当线程退出一个 synchronized 方法或代码块时,Monitor 的计数器的值就会递减 1,直到该值为 0 时才释放该锁。

通过上述过程,我们知道 synchronized 可重入锁的实现原理与 synchronized 的底层实现原理是不可分割的,都是通过锁对象的 Monitor 及其计数器的值递增或递减进行控制的,同一个线程对同一个对象的 Monitor 是可以多次重入的,重入时计数器的值递增 1,退出时再递减 1,直到计数器的值为 0 时释放锁,在此期间其他任何线程的访问都会被阻塞。

相关文章