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 该类提供了一些方法来检查锁的状态。这些方法并不是用来做同步控制。

扫描二维码关注公众号,回复: 7131592 查看本文章

同样的,该类是可序列化的,与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;

        }

    }


猜你喜欢

转载自www.cnblogs.com/charging-for-ycp/p/11432602.html