究竟什么是可重入锁?

Posted Lovnx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了究竟什么是可重入锁?相关的知识,希望对你有一定的参考价值。

经历

很久之前就听说了可重入锁,可重入锁究竟是什么意思,以前是囫囵吞枣的,只要记住ReentrantLock和sychronized是可重入锁就行了,爱咋用咋用,好吧,原谅我的无知,最近对基础查漏补缺,发现竟然对其一问三不知,赶紧预习一波,觉得有必要写一篇博客来讲解,就当做什么都没有发生吧,嘿嘿。。。

释义

广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。ReentrantLock和synchronized都是可重入锁,下面是一个用synchronized实现的例子:

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();
        
    

整个过程没有发生死锁的情况,截取一部分输出结果如下:

Thread-8492
Thread-8492
Thread-8494
Thread-8494
Thread-8495
Thread-8495
Thread-8493
Thread-8493

set()和get()同时输出了线程名称,表明即使递归使用synchronized也没有发生死锁,证明其是可重入的。

不可重入锁

不可重入锁,与可重入锁相反,不可递归调用,递归调用就发生死锁。看到一个经典的讲解,使用自旋锁来模拟一个不可重入锁,代码如下:

import java.util.concurrent.atomic.AtomicReference;

public class UnreentrantLock 

    private AtomicReference<Thread> owner = new AtomicReference<Thread>();

    public void lock() 
        Thread current = Thread.currentThread();
        //这句是很经典的“自旋”语法,AtomicInteger中也有
        for (;;) 
            if (!owner.compareAndSet(null, current)) 
                return;
            
        
    

    public void unlock() 
        Thread current = Thread.currentThread();
        owner.compareAndSet(current, null);
    

代码也比较简单,使用原子引用来存放线程,同一线程两次调用lock()方法,如果不执行unlock()释放锁的话,第二次调用自旋的时候就会产生死锁,这个锁就不是可重入的,而实际上同一个线程不必每次都去释放锁再来获取锁,这样的调度切换是很耗资源的。稍微改一下,把它变成一个可重入锁:

import java.util.concurrent.atomic.AtomicReference;

public class UnreentrantLock 

    private AtomicReference<Thread> owner = new AtomicReference<Thread>();
    private int state = 0;

    public void lock() 
        Thread current = Thread.currentThread();
        if (current == owner.get()) 
            state++;
            return;
        
        //这句是很经典的“自旋”式语法,AtomicInteger中也有
        for (;;) 
            if (!owner.compareAndSet(null, current)) 
                return;
            
        
    

    public void unlock() 
        Thread current = Thread.currentThread();
        if (current == owner.get()) 
            if (state != 0) 
                state--;
             else 
                owner.compareAndSet(current, null);
            
        
    

在执行每次操作之前,判断当前锁持有者是否是当前对象,采用state计数,不用每次去释放锁。

ReentrantLock中可重入锁实现

这里看非公平锁的锁获取方法:

        final boolean nonfairTryAcquire(int acquires) 
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) 
                if (compareAndSetState(0, acquires)) 
                    setExclusiveOwnerThread(current);
                    return true;
                
            
            //就是这里
            else if (current == getExclusiveOwnerThread()) 
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            
            return false;
        

在AQS中维护了一个private volatile int state来计数重入次数,避免了频繁的持有释放操作,这样既提升了效率,又避免了死锁。

以上是关于究竟什么是可重入锁?的主要内容,如果未能解决你的问题,请参考以下文章

多线程之synchronoized实现可重入锁

什么是可重入锁(ReentrantLock)?

什么是可重入锁(ReentrantLock)?

什么是重入锁和AQS

可重入锁

可重入锁的获取和释放须要注意的一点儿事