• java.util.concurrent.locks包中的ReentrantReadWriteLock


    ReentrantReadWritelock是ReadWrite的一个实现类,与ReentrantLock有着相似的语法。

    A)Acquisition Order 该类并不强制读锁或者写锁按照优先级进行获取,但是它提供可供选择的公平策略。

    Non-fair mode:默认情况下,采用的是非公平策略,读锁和写锁并未明确入队顺序,其受限于可重入约束性。持续竞争的锁可能会使一个或者多个线程无法获得该锁,但是其在吞吐量上明显优于公平策略的锁。

    Fair mode:采用公平策略的锁,会保证竞争的线程尽量按照到达先后的顺序进行入队。当当前线程释放锁的时候,等待队列中等待时间最长的写线程将会被唤醒;或者等待时间更长的一组读线程将会被唤醒。

    如果写锁被其他线程持有,或者等待队列中有写线程,获取公平读锁的线程会被阻塞。当等待时间最长的写线程获得该锁然后释放后,读线程才会获得该读锁。当然,如果等待的写线程被取消了,等待队列中不存在写线程,则等待的读线程将会被分配读锁。

    只有在读锁和写锁未被持有的情况下,写线程才可以成功获取写锁。WriterLock和ReaderLock的tryLock()与ReentrantLock的tryLock()相似,如果成功获取锁直接返回,无论等待队列是否为null。

    B)Reentrancy 无论读锁还是写锁都支持可重入性。只有当写锁被释放的时候,读线程才有机会获取锁。另外,一个写线程可以获取读锁,反之不可以。在其他应用方面,持有写锁的线程调用读锁的方法,,可重入性是非常有用的。如果一个持有读锁的线程获取写锁,将不会成功。

    C)Lock downgrading(锁降级) 当获取写锁的时候,可重入性允许写锁降级为读锁,然后获取读锁,再释放写锁。然后,读锁是不可以更新为写锁的 。

    D)Interruption of Lock Acquisition 读锁与写锁在获取锁的过程中支持中断机制。

    E)Condition Support 写锁支持Condition,但是读锁不支持。如果ReadLock.new Condition,将会抛出异常。

    F)Instrumentation 该类提供了一些方法来检查锁的状态。这些方法并不是用来做同步控制。

    同样的,该类是可序列化的,与ReentrantLock相似。

    For example

    class CachedData {

     *   Object data;

     *   volatile boolean cacheValid;

     *   final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

     *   void processCachedData() {

     *     rwl.readLock().lock();

     *     if (!cacheValid) {

     *       // Must release read lock before acquiring write lock

     *       rwl.readLock().unlock();

     *       rwl.writeLock().lock();

     *       try {

     *         // Recheck state because another thread might have

     *         // acquired write lock and changed state before we did.

     *         if (!cacheValid) {

     *           data = ...

     *           cacheValid = true;

     *         }

     *         // Downgrade by acquiring read lock before releasing write lock

     *         rwl.readLock().lock();

     *       } finally {

     *         rwl.writeLock().unlock(); // Unlock write, still hold read

     *       }

     *     }

     *

     *     try {

     *       use(data);

     *     } finally {

     *       rwl.readLock().unlock();

     *     }

     *   }

     * }}

    ReentrantReadWriteLock可以用在一些集合类中用来改善并发情况。读写锁主要应用于读多写少的情景。

    For example the using of TreeMap

    class RWDictionary {

     *   private final Map<String, Data> m = new TreeMap<String, Data>();

     *   private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

     *   private final Lock r = rwl.readLock();

     *   private final Lock w = rwl.writeLock();

     *

     *   public Data get(String key) {

     *     r.lock();

     *     try { return m.get(key); }

     *     finally { r.unlock(); }

     *   }

     *   public String[] allKeys() {

     *     r.lock();

     *     try { return m.keySet().toArray(); }

     *     finally { r.unlock(); }

     *   }

     *   public Data put(String key, Data value) {

     *     w.lock();

     *     try { return m.put(key, value); }

     *     finally { w.unlock(); }

     *   }

     *   public void clear() {

     *     w.lock();

     *     try { m.clear(); }

     *     finally { w.unlock(); }

     *   }

     * }}

    该锁中,读锁的数量和写锁的数量的最大值均为65535。因为state是一个int,前2个字节(16位)代表写锁,后四个字节(16位)代表读锁,均为。

    1      WriteLock

    1.1   Lock()

     if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

                selfInterrupt();

    1.1.1  trayAcquire[a1] ()

    1.存在读锁或者 存在写锁并且持有该锁的线程非本线程,return false;

    2.存在写锁并且该线程持有该锁,则检查写锁的count,if count>MAX_COUNT, throw exception;

    3.否则,count=0,线程开始竞争该锁:检查是否需要阻塞(非公平锁不需要阻塞,公平锁需要检查是否存在前继节点);不阻塞话,进行CAS设置state。如果OK的话,设置该锁的线程为本线程。

    protected final boolean tryAcquire(int acquires) {

                Thread current = Thread.currentThread();

                int c = getState();

                int w = exclusiveCount(c);

                if (c != 0) {

                    // (Note: if c != 0 and w == 0 then shared count != 0)

                    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;

                }

                if (writerShouldBlock() ||

                    !compareAndSetState(c, c + acquires))

                    return false;

                setExclusiveOwnerThread(current);

                return true;

            }

    1.1.2  addWaiter()

    同AQS

    1.1.3  acquireQueued()

    同AQS

    1.2   tryLock()

    sync.tryWriteLock();

    1.2.1  tryWriteLock()

    与trayAcquire相比,lack of the function - writerShouldBlock()

    final boolean tryWriteLock() {

                Thread current = Thread.currentThread();

                int c = getState();

                if (c != 0) {

                    int w = exclusiveCount(c);

                    if (w == 0 || current != getExclusiveOwnerThread())

                        return false;

                    if (w == MAX_COUNT)

                        throw new Error("Maximum lock count exceeded");

                }

                if (!compareAndSetState(c, c + 1))

                    return false;

                setExclusiveOwnerThread(current);

                return true;

            }

    1.3   tryLock(long timeout, TimeUnit unit)

     return sync.tryAcquireNanos(1, unit.toNanos(timeout));

    1.3.1  tryAcquireNanos(int arg, long nanosTimeout)

            if (Thread.interrupted())

                throw new InterruptedException();

            return tryAcquire(arg) ||

                doAcquireNanos(arg, nanosTimeout);

    1.3.1.1 doAcquireNanos(int arg, long nanosTimeout)

    增加一个时间节点的判断

    if (nanosTimeout <= 0L)

                return false;

            final long deadline = System.nanoTime() + nanosTimeout;

            final Node node = addWaiter(Node.EXCLUSIVE);

            boolean failed = true;

            try {

                for (;;) {

                    final Node p = node.predecessor();

                    if (p == head && tryAcquire(arg)) {

                        setHead(node);

                        p.next = null; // help GC

                        failed = false;

                        return true;

                    }

                    nanosTimeout = deadline - System.nanoTime();

                    if (nanosTimeout <= 0L)

                        return false;

                    if (shouldParkAfterFailedAcquire(p, node) &&

                        nanosTimeout > spinForTimeoutThreshold)

                        LockSupport.parkNanos(this, nanosTimeout);

                    if (Thread.interrupted())

                        throw new InterruptedException();

                }

            } finally {

                if (failed)

                    cancelAcquire(node);

            }

    1.4   unlock()

    sync.release(1);  同AQS

    1.5   Release()

    如果本线程成功释放锁,则会判断是否存在后继节点。如果存在并且head的waitstatus!=0则唤醒后继节点中的线程。

     if (tryRelease(arg)) {

                Node h = head;

                if (h != null && h.waitStatus != 0)

                    unparkSuccessor(h);

                return true;

            }

            return false;

    1.5.1  tryRelease()

    如果该线程不持有该锁,释放该锁,抛出illegalMonitorStateException;

    释放锁后,判断state的写锁的数量是否为0,如果为0,则独占锁=null,否则该线程继续持有该锁。

    protected final boolean tryRelease(int releases) {

                if (!isHeldExclusively())

                    throw new IllegalMonitorStateException();

                int nextc = getState() - releases;

                boolean free = exclusiveCount(nextc) == 0;

                if (free)

                    setExclusiveOwnerThread(null);

                setState(nextc);

                return free;

            }

    1.5.2  unparkSuccessor()[a2] 

    如果头结点head.waitStatus小于0,则CAS其status为0(初始状态);

    遍历循环同步队列,查询距离head最近的waitStatus<=0的结点,唤醒该结点的thread。

    waitStatus>0(cancelled)。

    同AQS

     private void unparkSuccessor(Node node) {

            int ws = node.waitStatus;

            if (ws < 0)

                compareAndSetWaitStatus(node, ws, 0);

            Node s = node.next;

            if (s == null || s.waitStatus > 0) {

                s = null;

                for (Node t = tail; t != null && t != node; t = t.prev)

                    if (t.waitStatus <= 0)

                        s = t;

            }

            if (s != null)

                LockSupport.unpark(s.thread);

        }

    2      ReadLock

    2.1   Lock()

    sync.acquireShared(1);

    public final void acquireShared(int arg) {

            if (tryAcquireShared(arg) < 0)

                doAcquireShared(arg);

        }

    2.1.1  tryAcquireShared[a3] ()

    1.如果写锁被其他线程持有,return false;

    2.是否阻塞,分公平与非公平两种策略。

    如果是非公平,为了防止写线程饥饿,如果同步队列中的第一个节点为写线程,则进行阻塞。如果是公平,则判断是否是第一个节点。

    3.如果不阻塞,则Cas state。

    如果开始不存在读锁,则count=1;

    如果firstReader=current,则count++;

    否则,缓存cachedHoldCounter

    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);

                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);

            }

    2.1.1.1 NofairSync.readerShouldBlock()

    非公平,为了防止写线程饥饿,如果同步队列中的第一个节点为写线程,则进行阻塞。

    return apparentlyFirstQueuedIsExclusive();

    final boolean apparentlyFirstQueuedIsExclusive() {

            Node h, s;

            return (h = head) != null &&

                (s = h.next)  != null &&

                !s.isShared()         &&

                s.thread != null;

        }

    2.1.1.2 FairSync.readerShouldBlock()

     return 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());

        }

    2.1.1.3 fullTryAcquireShared(Thread current)

    final int fullTryAcquireShared(Thread current) {

                /*

                 * This code is in part redundant with that in

                 * tryAcquireShared but is simpler overall by not

                 * complicating tryAcquireShared with interactions between

                 * retries and lazily reading hold counts.

                 */

                HoldCounter rh = null;

                for (;;) {

                    int c = getState();

                    if (exclusiveCount(c) != 0) {

                        if (getExclusiveOwnerThread() != current)

                            return -1;

                        // else we hold the exclusive lock; blocking here

                        // would cause deadlock.

                    } else if (readerShouldBlock()) {

                        // Make sure we're not acquiring read lock reentrantly

                        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();

                                }

                            }

                            if (rh.count == 0)

                                return -1;

                        }

                    }

                    if (sharedCount(c) == MAX_COUNT)

                        throw new Error("Maximum lock count exceeded");

                    if (compareAndSetState(c, c + SHARED_UNIT)) {

                        if (sharedCount(c) == 0) {

                            firstReader = current;

                            firstReaderHoldCount = 1;

                        } else if (firstReader == current) {

                            firstReaderHoldCount++;

                        } else {

                            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

                        }

                        return 1;

                    }

                }

            }

    2.1.2  doAcquireShared(int arg)

    同 AQS

    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);

                        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);

            }

        }

    2.2   tryLock()

    return sync.tryReadLock();

    2.2.1  tryReadLock()

    final boolean tryReadLock() {

                Thread current = Thread.currentThread();

                for (;;) {

                    int c = getState();

                    if (exclusiveCount(c) != 0 &&

                        getExclusiveOwnerThread() != current)

                        return false;

                    int r = sharedCount(c);

                    if (r == MAX_COUNT)

                        throw new Error("Maximum lock count exceeded");

                    if (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 true;

                    }

                }

            }

    2.3   Unlock

    public void unlock() {

                sync.releaseShared(1);

            }

    2.4   releaseShared(int arg)

    public final boolean releaseShared(int arg) {

            if (tryReleaseShared(arg)) {

                doReleaseShared();

                return true;

            }

            return false;

        }

    2.4.1  tryReleaseShared()

    protected final boolean tryReleaseShared(int unused) {

                Thread current = Thread.currentThread();

                if (firstReader == current) {

                    // assert firstReaderHoldCount > 0;

                    if (firstReaderHoldCount == 1)

                        firstReader = null;

                    else

                        firstReaderHoldCount--;

                } else {

                    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;

                }

                for (;;) {

                    int c = getState();

                    int nextc = c - SHARED_UNIT;

                    if (compareAndSetState(c, nextc))

                        // Releasing the read lock has no effect on readers,

                        // but it may allow waiting writers to proceed if

                        // both read and write locks are now free.

                        return nextc == 0;

                }

            }

    2.4.2  doRelease[a4] Shared()

    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;

            }

        }


  • 相关阅读:
    微信小程序 | 小程序的转发问题
    开发辅助 | 前端开发工程师必懂的 UI 知识
    微信小程序 | canvas绘图
    服务端 | Linux 学习总结 (一)
    移动端适配 | 适配方案总结
    开发工具 | 利用 deployd 搭建个人博客
    1.10 组织好代码文件,要有“用户思维”
    1.9 组织好代码段,让人对它“一见钟情”
    《计算机是怎样跑起来的》读书笔记(1)
    实用网站收藏
  • 原文地址:https://www.cnblogs.com/charging-for-ycp/p/11432602.html
Copyright © 2020-2023  润新知