ReentrantReadWriteLock是jdk中提供的一种相比于ReentrantLock能提供更高的读效率的锁

一、基本使用

public static void main(String[] args) throws InterruptedException {

        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        //获取读锁
        ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
        //获取写锁
        ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //用读锁对象读锁
                    readLock.lock();
                    log.info("加读锁成功");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    readLock.unlock();
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //用写锁对象加写锁
                    writeLock.lock();
                    log.info("加写锁成功");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    writeLock.unlock();
                }
            }
        });
        t1.start();
        t2.start();

    }

虽然加读锁和写锁用了2个锁对象,但它们都使用的ReentrantReadWriteLock中的同一个同步器对象。

二、特点

相比于ReentrantLock ,它支持读读操作的并发,即可以支持多个线程的同时读,但读写操作之间还是会被阻塞住。

进行读操作就要加读锁,调用readerLock对象的lock方法,写操作加写锁,调用writerLock对象的lock方法。

持有读锁时再次重入要加写锁(锁升级) 这种是不支持的,会一直等待,

持有写锁时再次重入要加读锁(锁降级) 这种是支持的。

三、原理分析

相比于ReentrantLock,ReentrantReadWriteLock 中的state低8为表示写锁的加锁状态,高8为表示读锁的加锁状态。

3.1 构造方法

先看写构造方法

public class ReentrantReadWriteLock
    implements ReadWriteLock, java.io.Serializable {
    // 加读锁和写锁时分别使用这2个内部类的对象来操作
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock */
    private final ReentrantReadWriteLock.WriteLock writerLock;
    /** Performs all synchronization mechanics */
    final Sync sync;
    /**
     * Creates a new {@code ReentrantReadWriteLock} with
     * default (nonfair) ordering properties.
     */
    public ReentrantReadWriteLock() {
        this(false);
    }

   //也分了公平锁和非公平锁的情况
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }
	//获取读锁/写锁对象的方法
    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }
}

3.2 加锁流程

加写锁会调用写锁对象的lock方法,加读锁会调用读锁对象的lock方法

public class ReentrantReadWriteLock
    implements ReadWriteLock, java.io.Serializable {
    //写锁对象内部类
    public static class WriteLock implements Lock, java.io.Serializable {
        private final Sync sync;
        
        protected WriteLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }
        
        public void lock() {
            //可以看到这个lock方法直接调用了aqs中的acquire方法
            //尝试加锁的tryAcquire会在ReentrantReadWriteLock中的同步器子类中实现,
            sync.acquire(1);
        }
    }
    
    //读锁对象内部类
    public static class ReadLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = -5992448646407690164L;
        private final Sync sync;
        
        protected ReadLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }
        
        //这是加读锁的方法,lock方法调用aqs中的acquireShared方法,具体逻辑需要看aqs中的实现
        public void lock() {
            sync.acquireShared(1);
        }
    }
    
    //同步器实现类
    abstract static class Sync extends AbstractQueuedSynchronizer {
        
           //这是加写锁的实现
           protected final boolean tryAcquire(int acquires) {
                    
            Thread current = Thread.currentThread();
            int c = getState();
            //这个方法返回state低8为的值即写锁部分的值
            int w = exclusiveCount(c);
            //c不等于0表示这个同步器别的线程已经加过锁了
            if (c != 0) {
                // w=0表示已经加的是读锁,如果owner不是当前线程就要返回加锁失败
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                //走到这里表示w!=0且owner是当前线程,即发生了锁重入
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                //修改state,返回true
                setState(c + acquires);
                return true;
            }
            //如果c=0会走到这里, writerShouldBlock是个抽象方法,在非公平锁的实现中会返回false,
            //就继续执行后边的compareAndSetState,如果成功了表示加锁成功,失败了就返回false表示加锁
            //失败,在公平锁的实现中writerShouldBlock会根据当前阻塞队列中是否已经有等待的线程来决定
            //返回值。
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }
        
        //加读锁公平模式时会调用到的方法
        protected final int tryAcquireShared(int unused) {
            Thread current = Thread.currentThread();
            int c = getState();
            //如果获取到state低八位的值不是0表示当前有写锁,锁降级不被允许所以返回加锁失败
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            //走到这里表示没有写锁
            //获取读锁的状态值
            int r = sharedCount(c);
            // readerShouldBlock方法是用来处理公平锁的,在公平/非公平两个内部类中有不同的实现,
            //公平实现中会看当前队列中有没有在等待的读锁,
            //如果有当前线程就不去抢锁了.
            //非公平实现中直接返回true,当前线程会去抢锁。
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;
                }
                return 1;
            }
            return fullTryAcquireShared(current);
        }
    }
}

aqs部分源码

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    public final void acquire(int arg) {
    	//其中tryAcquire由子类实现,acquireQueued把线程加入阻塞队列等待
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    
    //加读锁的方法
    public final void acquireShared(int arg) {
    	// tryAcquireShared方法由子类来实现,返回小于0的数表示加锁失败,才需要执行后续逻辑
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
    
    
    //加读锁的方法
    private void doAcquireShared(int arg) {
    	//创建读锁等待节点
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
            	//获取当前节点的前一个节点
                final Node p = node.predecessor();
                if (p == head) {
                	//这块是读线程被唤醒后执行的操作,继续抢锁
                    int r = tryAcquireShared(arg);
                    //这个r大于0表示当前有多个线程在等待读锁,
                    //进if后 setHeadAndPropagate 判断下下一个节点是不是也在等待读锁,如果是把它也唤醒
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())//这个方法让当前线程阻塞
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
}

至于解锁流程比较简单就不具体分析了。