美文网首页
ReentrantLock源码解析

ReentrantLock源码解析

作者: hcy0411 | 来源:发表于2018-09-17 15:17 被阅读0次

    简介

    ReentrantLock是一个可重入的独享锁,是平时常用的一个锁,用法和实现都比较简单,如下:

        private static ReentrantLock lock = new ReentrantLock();
        private static void testLock() {
    
            for (int i=0;i<100000;i++) {
                lock.lock();
                try {
                    test++;
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                } finally {
                    lock.unlock();
                }
            }
        }
        private static void testLock2() {
    
            for (int i=0;i<100000;i++) {
                if(lock.tryLock()) {
                    try {
                        test++;
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    } finally {
                        lock.unlock();
                    }
                }
            }
        }
        private static void testLock3() throws Exception {
    
            for (int i=0;i<100000;i++) {
                if(lock.tryLock(1, TimeUnit.MINUTES)) {
                    try {
                        test++;
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    } finally {
                        lock.unlock();
                    }
                }
            }
        }
    

    还有ReentrantLock支持可重入。

    ReentrantLock是基于aqs独占模式实现的,
    本文重点介绍下公平和非公平模式,和ReentrantLock实现原理。

    同步器-Sync

        abstract static class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = -5179523762034025860L;
    
    
            abstract void lock();
    
            //ReentrantLock 默认是非公平模式,这里给出了非公平模式下的尝试获取锁
            final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                // AbstractQueuedSynchronizer 的state常用于保存资源被占用状态,这里保存ReentrantLock锁状态,=0表示现在锁现在没被占用
                int c = getState();
                if (c == 0) {
                    // 设置成占用状态,返回true,并设置lock拥有线程为当前线程,支持可重入
                    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;
            }
    
            // 实现tryRelease,尝试释放锁,
            protected final boolean tryRelease(int releases) {
                int c = getState() - releases;
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) {
                    free = true;
                    setExclusiveOwnerThread(null);
                }
                setState(c);
                return free;
            }
            ```
        }
    

    了解AbstractQueuedSynchronizer 都知道,AbstractQueuedSynchronizer暴露tryAcquire,tryRelease让实现者自己去实现,ReentrantLock的Sync实现了这两个方法,用于判断是否可以获取锁和释放锁,接下来介绍非公平模式和公平模式

    非公平模式-NonfairSync

    ReentrantLock支持公平、非公平两种策略,并通过继承AQS实现了对应两种策略的同步器NonfairSync与FairSync,默认是非公平策略

        static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
    
    
            // 这里实现 lock方法,即ReentrantLock.lock方法
            final void lock() {
                // 这里判断是否有其他资源占用,cas设置成功,说明当前线程获取成功,这里
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    // 否则申请一个资源,就是这里会调用tryAcquire方法
                    acquire(1);
            }
            
            //尝试获取锁资源,调用的是非公平获取锁
            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
        }
    

    公平模式-FairSync

        /**
         * Sync object for fair locks
         */
        static final class FairSync extends Sync {
            private static final long serialVersionUID = -3000897897090466540L;
    
            //获取锁
            final void lock() {
                acquire(1);
            }
    
            //公平模式和非公平模式区别就在这里,多了hasQueuedPredecessors判断,就是看是否已有等待获取锁资源线程并且不是当前线程,如果有不去竞争
            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;
            }
        }
    

    公平模式和非公平模式,区别就在于一个hasQueuedPredecessors函数,该函数用于判断是否已有节点在等待资源了并且不是该线程等待的,附上代码

        public final boolean hasQueuedPredecessors() {
            // The correctness of this depends on head being initialized
            // before tail and on head.next being accurate if the current
            // thread is first in queue.
            Node t = tail; // Read fields in reverse initialization order
            Node h = head;
            Node s;
            return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
        }
    

    以上就是公平模式和非公平模式实现,都是实现了AbstractQueuedSynchronizer 的tryAcquire和tryRelease方法,用于判断是否能够获取资源和释放资源,实现简单。

    ReentrantLock的方法就不介绍了,都是调sync实现的方法,ReentrantLock最重要实现就是实现AbstractQueuedSynchronizer 的tryAcquire和tryRelease方法,支持可重入,还有支持Condition等待通知实现。

    相关文章

      网友评论

          本文标题:ReentrantLock源码解析

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