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