美文网首页
ReentrantLock使用与源码解析

ReentrantLock使用与源码解析

作者: 摆渡时光 | 来源:发表于2019-04-22 15:48 被阅读0次

    一、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使用与源码解析

          本文链接:https://www.haomeiwen.com/subject/jeiygqtx.html