ReentrantLock使用与源码解析

一、ReentrantLock使用

ReentrantLock是一种可重入锁,已经获得锁的线程可以继续获取锁也就是lock+1,但是同理加锁次数与释放锁次数必须相同才是正真的释放了锁。

public class ReentrantLockTest {

    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {

        // 运行线程A
        ThreadA threadA = new ThreadA("ReentrantLockThreadA");
        threadA.start();
        // 运行线程B
        ThreadB threadB = new ThreadB("ReentrantLockThreadB");
        threadB.start();

    }

    static class ThreadA extends Thread {

        public ThreadA(String name) {
            super(name);
        }

        public void run() {

            System.out.println("进入线程 " + Thread.currentThread().getName());
            // 加锁
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " 加锁1次");
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " 加锁2次");
            // 释放锁
            lock.unlock();
            System.out.println(Thread.currentThread().getName() + " 释放锁1次");
            lock.unlock();
            System.out.println(Thread.currentThread().getName() + " 释放锁2次");
        }
    }

    static class ThreadB extends Thread {

        public ThreadB(String name) {
            super(name);
        }

        public void run() {

            System.out.println("进入线程 " + Thread.currentThread().getName());
            // 加锁
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " 加锁1次");
            // 释放锁
            lock.unlock();
            System.out.println(Thread.currentThread().getName() + " 释放锁1次");
        }
    }
}

打印结果

进入线程 ReentrantLockThreadA
ReentrantLockThreadA 加锁1次
ReentrantLockThreadA 加锁2次
ReentrantLockThreadA 释放锁1次
ReentrantLockThreadA 释放锁2次
进入线程 ReentrantLockThreadB
ReentrantLockThreadB 加锁1次
ReentrantLockThreadB 释放锁1次

上述打印结果显示比较明显,先对A线程加锁两次后再释放锁两次,然后线程B在A释放锁后,继续获得锁,并执行结束。
那么如果将线程A第二次释放锁的代码删除掉,也就是这两行代码

            lock.unlock();
            System.out.println(Thread.currentThread().getName() + " 释放锁2次");

会发生什么?
执行结果为

进入线程 ReentrantLockThreadA
ReentrantLockThreadA 加锁1次
ReentrantLockThreadA 加锁2次
ReentrantLockThreadA 释放锁1次
进入线程 ReentrantLockThreadB

线程B永远处于等待状态,拿不到锁,因为线程A少释放了一次锁。

二、ReentrantLock源码解析

ReentrantLock实现了Lock接口

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;
    public void lock()
    // 发生中断会抛出InterruptedException
    public void lockInterruptibly()
    public boolean tryLock()
    public boolean tryLock(long timeout, TimeUnit unit)
    public void unlock()
    public Condition newCondition()
    // 获取加锁的次数
    public int getHoldCount()
    // 当前线程是否获得了锁
    public boolean isHeldByCurrentThread()
    // 是否已被加锁,可能是当前线程或者其它线程加的锁
    public boolean isLocked()
    // 公平锁会使用,判断队列中是否有线程等待获取锁
    public final boolean hasQueuedThreads()
    public final boolean hasQueuedThread(Thread thread)
    // 公平锁队列长度
    public final int getQueueLength()
    public boolean hasWaiters(Condition condition)
    public int getWaitQueueLength(Condition condition)
}

重点看sync以及加锁、释放锁和部分注释的方法。ReentrantLock内部实现包含了两种锁,非公平锁(默认)和公平锁,两种锁基于Sync实现。先看下Sync实现

    abstract static class Sync extends AbstractQueuedSynchronizer {

        abstract void lock();

        // 非公平获取锁的实现, 参数acquires表示几次加锁,一般为1
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            // 获取当前已加锁次数,状态0表示未加锁
            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;
        }

        // 释放锁
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            // 只有获得锁的线程才能释放锁
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                // 当释放锁次数和加锁次数一样时,返回true
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
}

基于Sync有两种实现非公平锁NonfairSync和公平锁FairSync,直接看下NonfairSync的实现

    static final class NonfairSync extends Sync {

        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

lock方法获取锁的最终实现还是tryAcquire方法获得锁。
非公平锁NonfairSync与公平锁有什么区别呢?先看下非公平锁NonfairSync的实现

    static final class FairSync extends Sync {

        final void lock() {
            acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

唯一有区别的就是这一行代码

                if (!hasQueuedPredecessors() &&

也就是说,公平锁根据加锁的先后顺序将线程放到队列中,当锁被上一个线程释放后,会从队列中按照顺序,取线程给予锁权限。非公平锁就是完全看"天意",大家不停try,谁也不确定什么时候能拿到锁。

你可能感兴趣的:(ReentrantLock使用与源码解析)