Art Java concurrent programming (12) locks and write locks reentrant

Reentrant lock

Reentrant lock ReentrantLock, indicating that the lock is capable of supporting a thread locking duplication of resources. Support for fair and unfair selection at lock.
synchronized keyword implicit support re-entry, such as a synchronized modification of the recursive method, when the method of execution, execution threads still repeatedly to get the lock after acquiring the lock.
Although not able to ReentrantLock as implicit support of re-entry, like the synchronized keyword, but when you call the lock () method, has been acquired to lock the thread can call the lock again () method to get the lock without being blocked.

Implement re-entry

Re-entry refers to any thread can acquire the lock again after acquiring lock to lock without being blocked.
(1) thread acquires the lock again . Lock need to identify whether the thread to acquire the lock for the current thread lock occupied, and if so, successfully acquired again.
(2) lock the final release . N Repeat this thread acquired the lock, then the n-th release the lock, other threads can obtain the lock. The final lock acquisition request to release the lock for the self-energizing counted, the count represents the current time acquired lock is repeated, and when the lock is released, decrement the count when the count is equal to 0 indicates that the lock has been released successfully.

		非公平性(默认)
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {              
                if (compareAndSetState(0, acquires)) {    CAS设置同步状态,即获取锁
                    setExclusiveOwnerThread(current);     记录当前线程
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {如果是获取锁的线程再次请求,则将同步状态值进行增加并返回 true,表示获取同步状态成功。
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;           
            if (Thread.currentThread() != getExclusiveOwnerThread())    判断当前线程是否为获得锁的线程     
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {                          最终释放条件c=0
                free = true;
                setExclusiveOwnerThread(null);     清空获得锁的线程
            }
            setState(c);
            return free;           如果该锁被获取锁被获取了n次,那么前(n-1)次都返回false,只有同步状态完全被释放,才返回true
        }

Fair and equitable access to the difference between the non-lock

Fairness or not is for the absolute chronological order in terms of acquiring the lock, if a lock is fair, then the order to obtain the lock should be in line with the request, which is FIFO

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

This method nonfairTryAcquire (int acquires) the comparison, the only difference of position determination conditions more hasQueuedPredecessors () method, i.e., added is determined whether the synchronous queue in the current node has a predecessor node, if this method returns true, it indicates that there is a thread than the current thread earlier request to acquire the lock, it is necessary to wait for a thread precursor to obtain and release the lock after lock to continue getting.

Read-Write Lock

ReadWriteLock only defines two methods to obtain read and write locks, that readLock () method and writeLock method, and its implementation ReentrantReadWriteLcok.

Method name description
int getReadLockCount() Returns the number of read lock is acquired current. The first frequency and the number of reservoirs is not equal to the number of threads acquiring a read lock, for example, only one thread, which continuously acquires (reentry) n times read lock, then the read lock is occupied by 1, but the method returns n
int getReadHoldCount() Returns the current thread to obtain the number of read locks. This method was added in Java6 to ReentrantReadWriteLock using TreadLocal save the number of the current thread acquired, which also makes the realization Java6 become more complex
boolean isWriteLocked() Determining whether a write lock is acquired
int getWriteHoldCount() Returns the number of write lock is acquired current
public class Cache {
    static Map<String,Object> map=new HashMap<>();
    static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();
    static Lock r=readWriteLock.readLock();
    static Lock w=readWriteLock.writeLock();
    获取一个key对应的value
    public static final Object get(String key){
        r.lock();
        try {
            return map.get(key);
        }finally {
            r.unlock();
        }
    }
    设置key对应得value,并返回旧得value
    public static final Object put(String key,String value){
        w.lock();
        try {
            return map.put(key,value);
        }finally {
            w.unlock();
        }
    }
    清空所有的内容
    public static final void  clear(){
        w.lock();
        try {
            map.clear();
        }finally {
            w.unlock();
        }
    }
}

Cache combination of a non-thread-safe as the implementation of HashMap cache, while the use of read-write lock to read and write locks to ensure that Cache is a thread safe. In the read operation get (String key) method to get a read lock, which allows concurrent access is not blocked when the method. Write put (String key, Object value) method and clear () method, you must obtain advance written when updating HashMap lock, after obtaining a write lock, other threads to obtain read and write locks for are blocked, and only write after the lock is released, the other read and write operations to continue. Cache uses concurrency read-write lock upgrade, but also to ensure the visibility of each write operation for all of the read and write operations, while simplifying programming.

Read and write lock analysis

Design of read-write, write lock acquisition and release, obtain and release the lock and read lock downgrade

Designed to read and write state

Write lock synchronizer also rely custom function to achieve synchronization, the synchronous state thereof is read-write synchronizer.
If the maintenance state of a plurality of integer variable, it must need to "cut using bitwise" variable, the variable write lock cut into two portions, the upper 16 bits represent read, write the lower 16 bits represent.
Here Insert Picture Description
Current sync state represents a thread has acquired a write lock, and re-entered twice, synchronization is also acquiring two consecutive read lock.
By bit operations, reading and writing to determine the respective states.
Suppose the current value of the synchronization status bits S, the write state is equal to S & 0X0000FFFF (high to erase the entire 16-bit), a read state is equal to S >>> (0 complement unsigned 16-bit right shift).
When a write state increases, equal to S + 1, is incremented by 1 when the read state, is equal to S + (1 << 16), i.e. S + 0x00010000
according to a divided state can draw inferences: S is not equal to 0, when write state (S & 0x0000FFFF) is equal to 0, then read state (S >>> 16) is greater than 0, i.e., the read lock has been acquired.

Write lock acquisition and release

Write lock is a support re-entry of an exclusive lock. If the current thread has acquired a write lock, increase the write state. If the current thread while acquiring write locks, read lock has been acquired (read status is not 0) or the thread is not already get the write lock thread, the current thread into a wait state.

        protected final boolean tryAcquire(int acquires) {
            /*
             * Walkthrough:
             * 1. If read count nonzero or write count nonzero
             *    and owner is a different thread, fail.
             * 2. If count would saturate, fail. (This can only
             *    happen if count is already nonzero.)
             * 3. Otherwise, this thread is eligible for lock if
             *    it is either a reentrant acquire or
             *    queue policy allows it. If so, update state
             *    and set owner.
             */
            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;
        }
Read lock acquisition and release

Read lock is a shared lock on re-entry support, it can be acquired simultaneously by multiple threads, when no other writers from accessing (or write state is 0), always read lock was successfully acquired, and made only (thread-safe) increase the read state. If the current thread has acquired a read lock, increase reading status. If the current thread while acquiring a read lock, write lock has been acquired by another thread, then into a wait state.

        protected final int tryAcquireShared(int unused) {
            /*
             * Walkthrough:
             * 1. If write lock held by another thread, fail.
             * 2. Otherwise, this thread is eligible for
             *    lock wrt state, so ask if it should block
             *    because of queue policy. If not, try
             *    to grant by CASing state and updating count.
             *    Note that step does not check for reentrant
             *    acquires, which is postponed to full version
             *    to avoid having to check hold count in
             *    the more typical non-reentrant case.
             * 3. If step 2 fails either because thread
             *    apparently not eligible or CAS fails or count
             *    saturated, chain to version with full retry loop.
             */
            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);
        }

In tryAcquireShared (int unused) method, if another thread has already acquired write lock, then the current thread to acquire a read lock failed to enter the wait state. If the current thread to obtain a write lock or read lock is not acquired, the current thread (thread-safe, relying on the CAS guarantee) to increase reading state, successfully acquired a read lock. Read each lock release (thread-safe, there may be multiple threads simultaneously read the release read lock) were reduced read state, reducing the value of (1 << 16).

Lock downgrade

Lock downgrade refers to the write lock downgrade become a read lock. If the current thread owns the write lock, then release it, and finally acquire a read lock, this process can not be called to complete the segment lock downgrade. Lock downgrade means to hold on (currently owned) write locks, and then to get a read lock, then release process (previously owned) write lock.

Published 24 original articles · won praise 1 · views 538

Guess you like

Origin blog.csdn.net/qq_45366515/article/details/105244806