JAVA-ReentrantReadWriteLock

之前讲到的ReentrantLock属于排他锁,虽然可以重入多次,但同一时间只允许一个线程获取锁。而今天讲到的ReentrantReadWriteLock维护了一对锁——读锁和写锁。读锁是共享锁,允许多个线程同时持有。读锁是排他锁,同一时刻只能有一个线程持有。通过分离读锁和写锁,使得并发性相比一般的排他锁(ReentrantLock,Synchronized)有很大的性能提升,因为大多数场景中读是多于写的;读写锁还提供了如下特性支持:

 1.公平性选择——公平模式下,先到的线程会先执行,非公平模式下,谁先抢到谁就先执行

 2.重入——读线程在获取读锁之后,能够再次获取读锁;写线程在获取写锁后可以再次获取写锁

 3.锁降级——写线程先获取写锁,在释放写锁之前获取读锁,然后再释放写锁,这时写锁就降级为了读锁

一. ReentrantReadWriteLock重要成员变量

static final int SHARED_SHIFT   = 16;
// 1 00000000 00000000,一个共享锁单元
static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
// 11111111 11111111
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
// 11111111 11111111
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

// 获取高16位,代表读锁数量(共享锁)
static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
// 获取低16位,代表写锁数量(排它锁)
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

 二.ReadLock

 

 

public static void main(String[] args) {
    ReadWriteLock lock = new ReentrantReadWriteLock();
    try {
        lock.readLock().lock();
    } finally {
        lock.readLock().unlock();
    }
}

 获取读锁

/**
  * 获取一把读锁
  * ReadLock
  */
public void lock() {
    sync.acquireShared(1);
}

/**
  * 共享模式获取锁
  * AbstractQueuedSynchronizerSource
  */
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        // 如果获取读锁失败,就放到同步队列中去等待
        doAcquireShared(arg);
}

/**
 * 共享锁(读锁)获取
 * Sync
 */
protected final int tryAcquireShared(int unused) {

	Thread current = Thread.currentThread();
    int c = getState();
    
    // 有其它线程已经获取了写锁,当前线程不能再获取读锁
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    // 共享锁(读锁)数量
    int r = sharedCount(c);
    /**
     * 1.判断是否需要阻塞(防止写锁饥饿)
     * 2.读锁数量未达到可重入的最大值限制
     * 3.CAS设置共享锁数量(+1)
     */
    if (!readerShouldBlock() &&
        r < MAX_COUNT &&
        compareAndSetState(c, c + SHARED_UNIT)) {
        if (r == 0) {
            // 第一次被线程获取
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            // 更新首次获取读锁线程的HoldCount
            firstReaderHoldCount++;
        } else {
            // 如果最后一次读锁是其它线程获取的
            // 就把readHolds和cachedHoldCounter更新为当前线程的
            HoldCounter rh = cachedHoldCounter; // 先查缓存
            if (rh == null || rh.tid != getThreadId(current))
            	// 缓存没有命中,或缓存中关联的线程不是当前线程,就获取当前线程的HoldCounter
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
            rh.count++;
        }
        // 返回1,说明已经成功获取了一把读锁
        return 1;
    }
    // 第一次获取读锁失败后,尝试循环获取
    return fullTryAcquireShared(current);
}

/**
 * 在tryAcquireShared中进行了一次快速锁获取,但是由于CAS只能允许一个线程获取锁成功,
 * 且读锁是共享的,可能存在其它仍然可以获取锁的线程,所以在方法最后调用fullTryAcquireShared来进 
 * 行死循环的获取锁,这个方法很关键
 * Sync
 */
final int fullTryAcquireShared(Thread current) {
	HoldCounter rh = null;
	// CAS自旋
    for (;;) {
        int c = getState();
        // 再次判断独占线程
        if (exclusiveCount(c) != 0) {
            if (getExclusiveOwnerThread() != current)
                return -1;
        } 
        // 此处注意:如果exclusiveCount(c) != 0 && getExclusiveOwnerThread() == current
        // 就会直接去循环获取锁,不会被添加进同步队列中,这是为了防止锁降级导致的死锁
        else if (readerShouldBlock()) {
            if (firstReader == current) {
            	// 此处表明,当前线程是读锁重入,可以继续获取读锁
                // assert firstReaderHoldCount > 0;
            } else {
                if (rh == null) {
                    rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current)) {
                        rh = readHolds.get();
                        if (rh.count == 0)
                            readHolds.remove();
                    }
                }
                // 此处表明,当前线程是第一次获取读锁,但同步队列中第二个节点又是写线程,为了防止写饥饿,就把该读线程放到同步队列中等待
                // 如果rh.count>0,说明当前线程是读锁重入,是允许再次获取读锁的
                if (rh.count == 0)
                    return -1;
            }
        }
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        if (compareAndSetState(c, c + SHARED_UNIT)) { 
        	// 这里CAS成功,说明成功获取一把读锁了
            if (sharedCount(c) == 0) {
            	// 如果发现读锁数量为0,那就将当前线程设置为firstReader,firstReaderHoldCount=1
                firstReader = current;
                firstReaderHoldCount = 1;
            } else if (firstReader == current) {
            	// 如果firstReader==current,就将firstReaderHoldCount+1
                firstReaderHoldCount++;
            } else {
            	// 如果不是当前线程拥有的读锁,就将cachedHoldCounter设置为当前线程
                if (rh == null)
                    rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                else if (rh.count == 0)
                    readHolds.set(rh);
                rh.count++;
                cachedHoldCounter = rh; // cache for release
            }
            // 返回大于0,代表获取到了读锁
            return 1;
        }
    }
}

/**
 * 判断同步队列第二个节点是否是写线程,防止写饥饿
 * NonfairSync
 */
final boolean readerShouldBlock() {
	return apparentlyFirstQueuedIsExclusive();
}

/**
 * 判断同步队列中是否存在等待时间更长的线程
 * FairSync
 */
final boolean readerShouldBlock() {
    return hasQueuedPredecessors();
}

/**
 * 将当前线程放到阻塞队列的尾部并阻塞
 * AbstractQueuedSynchronizer
 */
private void doAcquireShared(int arg) {
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        // CAS自旋
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
            	// 如果当前节点是第二个节点,就尝试竞争一次
                int r = tryAcquireShared(arg);
                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);
    }
}

/**
 * 设置头节点,如果子节点也是在等待共享锁,就唤醒
 * AbstractQueuedSynchronizer
 */
private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // Record old head for check below
    setHead(node);
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared()) 
        	// 如果后续节点仍然是共享节点,则继续唤醒
            doReleaseShared();
    }
}

/**
 * 如果头节点状态为SIGNAL,就唤醒下一个节点线程
 * 如果头节点状态为0,就把状态更新为PROPAGATE
 * AbstractQueuedSynchronizer
 */
private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) // 设置节点状态为无效
                    continue;            // loop to recheck cases
                // 唤醒下一个节点,如果下一个线程获取锁成功,会循环继续唤醒下一个节点
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

 释放读锁

 

/**
 * 释放读锁
 * ReadLock
 */
public void unlock() {
    sync.releaseShared(1);
}

/**
 * 共享模式下释放锁
 * AbstractQueuedSynchronizer
 */
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) { 
    	// 如果所有锁(包括读锁和写锁)都释放完了才会去唤醒下一个节点线程
        doReleaseShared();
        return true;
    }
    return false;
}

/**
 * 共享模式下释放锁
 * Sync
 */
protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    if (firstReader == current) {
    	// 如果firstReader为当前线程,就把firstReaderHoldCount减1或置空
        if (firstReaderHoldCount == 1)
            firstReader = null;
        else
            firstReaderHoldCount--;
    } else {
    	// 当前线程持有的读锁数量减1或置空
        HoldCounter rh = cachedHoldCounter;
        if (rh == null || rh.tid != getThreadId(current))
            rh = readHolds.get();
        int count = rh.count;
        if (count <= 1) {
            readHolds.remove();
            if (count <= 0)
                throw unmatchedUnlockException();
        }
        --rh.count;
    }
    // 所有线程持有的读锁数量减1
    for (;;) {
        int c = getState();
        int nextc = c - SHARED_UNIT;
        if (compareAndSetState(c, nextc))
            // 注意,所有锁(包括读锁和写锁)都释放完了才会返回true
            return nextc == 0;
    }
}

三.WriteLock 

 

public static void main(String[] args) {
    ReadWriteLock lock = new ReentrantReadWriteLock();
    try {
        lock.writeLock().lock();
    } finally {
        lock.writeLock().unlock();
    }
}

获取写锁 

/**
 * 获取写锁
 * WriteLock
 */
public void lock() {
    sync.acquire(1);
}

/**
 * 排他模式获取锁,如果获取不到,就会放到链表尾部,线程挂起
 * AbstractQueuedSynchronizer
 */
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

/**
 * 获取写锁
 * Sync
 */
protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    int c = getState();
    int w = exclusiveCount(c);
    if (c != 0) {
    	// c != 0 && w == 0,说明此时有线程(包括当前线程)拥有读锁,不允许获取写锁(不支持锁升级)
    	// c != 0 && w != 0 && current != getExclusiveOwnerThread() 说明有其它线程有过锁降级,不允许再次获取写锁
    	// c != 0 && w != 0 && current == getExclusiveOwnerThread() 说明当前线程有过锁降级,允许再次获取写锁
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // Reentrant acquire
        setState(c + acquires);
        return true;
    }
    // 写锁数量+1
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}

/**
  * NonfairSync
  */
final boolean writerShouldBlock() {
    return false; // writers can always barge
}

/**
 * 判断同步队列中是否存在等待时间更长的线程
 * FairSync
 */
final boolean writerShouldBlock() {
    return hasQueuedPredecessors();
}

/**
  * 前面已经把node节点放到了链表尾部排队了,此方法将当前线程挂起
  * AbstractQueuedSynchronizer
  */
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true; // 标记是否成功拿到资源
    try {
        boolean interrupted = false; // 标记等待过程中是否被中断过
        // CAS第二次自旋,直到获得锁
        for (;;) {
            final Node p = node.predecessor(); // 获取父节点
            // 如果父节点是head,即当前节点是第二个节点,那么便有资格去尝试获取资源
            // (可能是老大释放完资源唤醒自己,也可能被interrupt了)
            if (p == head && tryAcquire(arg)) {
            	// 注意此处和读锁不同,这里不会唤醒下一个节点线程
                setHead(node);
                p.next = null; // 释放对子节点的引用,便于后面GC对子节点的回收,此时意味着head节点被移除了链表
                failed = false;
                return interrupted; // 返回等待过程中是否被中断过
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
            	// 此处当前线程会挂起(park),等待前一个节点唤醒(unpark或interrupt)
                parkAndCheckInterrupt())
            	// 注意此处有中断,还是会循环到上面去获取锁,其实是忽略了中断情况
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}


释放写锁 

/**
 * 释放写锁
 * WriteLock
 */
public void unlock() {
    sync.release(1);
}

/**
 * 在排他模式中释放锁,如果当前线程的锁全部释放,就去激活下一个节点的线程
 * AbstractQueuedSynchronizer
 */
public final boolean release(int arg) {
    if (tryRelease(arg)) { // 如果返回true,说明当前线程加的写锁都已经释放完了
        Node h = head;
        // 如果还有下一个节点,就唤醒队列中的下一个线程
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

/**
 * 释放写锁
 * Sync
 */
protected final boolean tryRelease(int releases) {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    int nextc = getState() - releases;
    // 注意,写锁释放为就返回true
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
        setExclusiveOwnerThread(null);
    setState(nextc);
    return free;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值