Lock of jdk source code parsing (juc) - lock ReetrentLock in java

Lock in jdk source code analysis (juc) - lock ReetrentLock in java Orange dead blog Please add this prompt for forwarding

Lock of jdk source code parsing (juc) - lock ReetrentLock in java

Lock interface

Before the appearance of Lock interface, java realized the Lock function through synchronized keyword. After javase5, the Lock interface was added by contract

The use of Lock is similar to the structure of distributed Lock.

Lock lock = new ReentrantLock
lock.lock();
try{
}finally{
 lock.unlock();
}

The Lock interface provides features that the Synchronized keyword does not have

Attempt to acquire the lock nonblocking The current thread attempts to acquire the lock. If no other thread acquires the lock, it succeeds
Access lock that can be interrupted
Timeout acquire lock Acquire lock in specified time

API of Lock interface

api
void lock() Lock takes priority to acquire lock. It will respond to interrupt only after acquiring lock successfully.
void lockInterruptibly() throws InterruptedException Lockinterruptability takes precedence over normal or reentrant acquisition of response locks.
                                    |

| boolean tryLock() | |
| boolean tryLock(long time,TimeUtil unit) throws InterruptedException | |
|void unlock() | release lock|
|Condition newCondition | get the waiting notification component. The component is bound to the current lock. The current thread can only call the wait() method of the component if it obtains the lock. After calling, the current thread will release the lock|

Queue synchronizer

The implementation of lock is based on queue synchronizer, AbstractQueuedSynchronized (referred to as synchronizer), using an int member variable to represent the synchronization state, and using the built-in FIFO queue to complete the queuing work of resource acquisition thread

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;

    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     ad locum!!!
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is
         * implemented in subclasses, but both need nonfair
         * try for trylock method.
         *Unfair access lock
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                  //Compared with the way of fair lock, the method of hasQueuedPredecessors is less
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

        //Release current lock
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes this lock instance from a stream.
         * @param s the stream
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

    /**
     * Sync object for non-fair locks
     Unfair lock
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         *Get lock
         *
         */
        final void lock() {
            //If the current state is 0, then it is set to the current thread, that is to say, it is not fair to some extent, and may directly obtain the lock
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
            //Queue to acquire lock
                acquire(1);
        }

         //Queue to acquire lock
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }


    }

    /**
     * Sync object for fair locks
     Public lock
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        //Queue acquisition
        //Compared with unfair methods, we can see why this is called fair lock
        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
               //hasQueuedPredecessors records the order of entering the queue, and the first to acquire the lock
               //AbstractQueuedSynchronized maintains a Node queue
                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;
        }
    }

ReetrentLock

It supports re-entry locks and can support a thread to repeatedly lock resources. See the code above.

Readwritelock

Characteristic

Fair choice
Re entry
Lock down

Interface example

int getReadLockCount() The number of times the read lock is possible
int getReadHoldCount() The number of times the current thread may read a lock
int getWriteLockCount()
int getWriteHoldCount()

Use of Lock

Through Cache to explain read-write lock, HashMap is non thread safe, through read-write lock to achieve thread safety of Cache

public class Cache {
    static Map<String,Object> map = new HashMap<String,Object>();
    static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    static Lock r = rwl.readLock();
    static Lock w = rwl.writeLock();

    public static final Object get(String key){
        r.lock();
        try {
            return map.get(key);
        }finally {
            r.unlock();
        }
    }

    public static final Object put(String key,Object 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();
        }
    }

}

Condition interface and examples

public class ConditionUseCase {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public static void main(String[] args){

    }

    public void conditionWait() throws InterruptedException {
        lock.lock();
        try {
            condition.await();
        }finally {
            lock.unlock();
        }
    }
    public void conditionSignal(){
        lock.lock();
        try {
            condition.signal();
        }finally {
            lock.unlock();
        }
    }
}

Partial method description

void await() The current thread enters a wait state until it is notified or interrupted
void awaitUninterruptibly() The current thread enters the waiting state, not sensitive to interrupt
long awaitNanos(long nanosTimeout) The current thread enters the waiting state until it is notified, interrupted or timed out. The return value represents the remaining time. If the return value is 0 or negative, it can be considered that it has timed out
boolean awaitUntil(Date deadline) The current thread enters the waiting state until it is notified, interrupted or reaches a certain time. If it does not reach the specified time, it returns true. Otherwise, it returns false when it reaches the specified time
void signal() Wake up a thread waiting in the condition, which must acquire the lock associated with the condition before returning from the waiting method
void signlAll() Wake up all threads in the waiting condition. The thread that can return from the waiting method must obtain the lock associated with the condition
  • BoundedQueue interpretation Condition
public class BoundedQueue<T> {
    private Object[] items;
    private int addIndex,removeIndex,count;
    private Lock lock = new ReentrantLock();
    private Condition notEmpty = lock.newCondition();
    private Condition notFull = lock.newCondition();

    public BoundedQueue(int size){
        items = new Object[size];
    }

    public void add(T t) throws InterruptedException {
        lock.lock();
        try {
            while(count == items.length)
                notFull.await();
            items[addIndex] = t;
            if(++addIndex == items.length)
                addIndex = 0;
            ++count;
            notEmpty.signal();
        }finally {
            lock.unlock();
        }
    }

    public T remove() throws InterruptedException {
        lock.lock();
        try {
            while(count == 0)
                notEmpty.await();
            Object x = items[removeIndex];
            if(++removeIndex == items.length)
                removeIndex = 0;
            --count;
            notFull.signal();
            return (T) x;
        }finally {
            lock.unlock();
        }

    }
}

Keywords: Java JDK less

Added by beta0x64 on Sun, 08 Dec 2019 19:02:10 +0200