“This is the 26th day of my participation in the First Challenge 2022. For details: First Challenge 2022.”
The design idea and overall design structure of AQS are introduced in detail above. Now let’s introduce another interface related to Lock and AQS, Lock interface, and then borrow AQS and Lock interface to quickly implement a custom Lock.
AQS Related articles:
AQS (AbstractQueuedSynchronizer) source depth resolution (1) – design and general structure of AQS
AQS (AbstractQueuedSynchronizer) source depth resolution (2) – Lock interfaces and implementation of custom Lock
AQS (AbstractQueuedSynchronizer) source depth resolution (3) – synchronous queue and exclusive access to the principle of lock, lock is released [ten thousand words]
AQS (AbstractQueuedSynchronizer) source depth resolution (4), the principle of Shared locks, lock is released [ten thousand words]
AQS (AbstractQueuedSynchronizer) source depth resolution (5) – condition queue waiting, the realization of the notification and summary of AQS [ten thousand words]
1 the Lock interface
1.1 Overview of Lock Interfaces
public interface Lock
The Lock interface does not have much to do with AQS, but if you want to implement a formal, generic synchronization component (especially locking), you have to mention the Lock interface.
The Lock interface, also from JDK1.5, is described as the super interface for locks in JUC. All locks in JUC implement the Lock interface.
Since it is designed as an interface, it is probably clear by now that the interface is a specification. Lcok interface defines some abstract method, is used to get the Lock, Lock is released, etc., and all the locks the Lock interface implementation, although they may have different internal implementation, but open to outside the method called is the same, this is a specification, no matter how you achieve, is always to call you “one way”! For this reason, JUC locks are often referred to collectively as lock locks.
This excellent architecture design, different lock implementation unified lock acquisition, release and other conventional operation methods, convenient for external personnel to use and learn! A similar design can be seen with JDBC database drivers!
1.2 API method of Lock interface
Let’s see what methods we need to implement to implement the Lcok interface!
Method names | describe |
lock | The lock is acquired. If the lock cannot be acquired, the current thread is suspended without interruption until the lock is acquired. |
lockInterruptibly | Retrieves the lock. If it does, it returns immediately. If it does not, the current thread is suspended until the current thread is awakened or another thread interrupts the current thread. |
tryLock | This method acquires the lock and returns true if the lock can be obtained, or false immediately if the current lock cannot be obtained |
tryLcok(long time,TimeUnit unit) | Attempts to acquire the lock within the specified time. Returns true if a lock is acquired, and if the current lock cannot be acquired, the current thread is suspended until the current thread acquires the lock or is interrupted by another thread or the specified wait time expires. Return false if the lock has not been acquired. |
unlock | Release the lock held by the current thread |
newCondition | Returns a condition variable associated with the current lock. Before using this condition variable, the current thread must hold the lock. Calling the await method of Condition releases the lock atomically before waiting and acquires the lock atomically after waiting to be awakened |
3.3 Lock acquisition and interruption
Thread.wait (), Thread.join(), thread.sleep (), etc. As well as blocking if the lockInterruptibly method and tryLock (time, timeUnit) attempt to acquire the lock but do not, and both throw InterruptedException and set the thread’s interrupt status bit to false.
But for threads that are blocked by calling the lock() method or by failing to acquire a Synchronized lock, the interrupt method cannot interrupt and only sets the interrupt flag bit to true, and for normal threads, only sets the interrupt flag bit to true to inform the thread that it should be interrupted. A special case is that threads blocked by locksupport.park () can also be interrupted, but no exception is thrown and flag bits are not restored.
The following example illustrates the use of these four methods of the Lock interface: If thread A and thread B use the same Lock Lock, thread A obtains the Lock lock. Lock () first and holds it.
If B wants to acquire the lock, there are four ways:
- Lock.lock (): This will keep B rupt(even if it calls B.innterrupt ()), unless thread A calls lock.unlock () to release the LOCK.
- Lock.lockinterruptibly (): This will cause B to wait, but when b.nterrupt () is called, it will interrupt the wait and throw InterruptedException, otherwise it will wait like LOCK () until thread A releases the LOCK.
- Lock. tryLock(): this method does not wait and returns false if the LOCK is not acquired once.
- Lock. tryLock(10, timeunit.seconds) : This path will be on wait for 10 SECONDS, but a call to B.interrupt rupt() will interrupt the wait and throw InterruptedException. If thread A releases the lock within 10 seconds, thread B will acquire the lock and return true, otherwise it will return false to execute the following logic.
3.4 Differences between Synchronized and Lock
- Synchronized is a Java built-in keyword, which is implemented by JVM. Lock is a Java interface that can represent a Lock in JUC and is implemented in Java code.
- Synchronized automatically releases the lock (a thread releases the lock after executing the synchronization code; The Lock must be released manually in finally (unlock()). Otherwise, the thread is likely to deadlock. Lock acquires and releases locks explicitly, and synchronized acquires and releases locks implicitly.
- Lock can interrupt the wait with the lockInterruptibly () method and interrupt () method. You can also use tryLock () to set the wait timeout. Synchronized only waits for the lock to be released and does not respond to interrupts.
- Synchronized is an unfair Lock, whereas a Lock can be implemented as a fair Lock or an unfair Lock.
- A Lock Lock can have multiple Condition objects (Condition queues). Singal and signalAll can choose to wake up threads in different queues. The same synchronized block can have only one monitor object, a conditional queue.
- Lock can improve the efficiency of multiple threads for read operations, very flexible. (Read/write separation can be achieved with ReadWritelock).
- Synchronized uses Thread.holdLock(monitor object) to check whether the current Thread holds the Lock. Lock can be determined by using lock.trylock or isHeldExclusively.
2 Unreentrant exclusive lock simple implementation
After the above learning, we know the general design ideas and methods of AQS, as well as the standard Lock needs to implement the Lock interface, now we try to build a simple exclusive Lock.
As the name implies, an exclusive lock is that only one thread can acquire the lock at the same time, and other threads that acquire the lock can only wait in the synchronization queue. Only when the thread that obtains the lock releases the lock, the subsequent thread can acquire the lock. The following is an implementation of an exclusive lock based on AQS.
We need to rewrite the tryAcquire and tryRelease(int Releases) methods. Use setExclusiveOwnerThread to record the thread that acquired the lock. If the state is 0, the lock is not acquired. In addition, the simple implementation below has no consideration for reentrancy, so it is not reentrant!
As you can see from the implementation, with the AQS tool, we can implement a custom synchronization component is relatively easy!
/ * * *@author lx
*/
public class ExclusiveLock implements Lock {
/** * combine the implementation of AQS into the implementation of the lock * for the synchronization state, the implementation treats 1 as synchronized and 0 as unsynchronized */
private static class Sync extends AbstractQueuedSynchronizer {
/** * Rewrite the isHeldExclusively method **@returnWhether the lock is occupied */
@Override
protected boolean isHeldExclusively(a) {
// State is equal to 1
return getState() == 1;
}
/** * Overwrite the tryAcquire method to try to acquire the lock@paramAcquires parameter, we don't use * here@returnReturns true on success, false */ on failure
@Override
public boolean tryAcquire(int acquires) {
if (compareAndSetState(0.1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
/** * override tryRelease to release the lock * when state is 1, set state to 0 **@paramSo the releases parameter, we're not using * here@returnReturn true on success, false */ on failure
@Override
protected boolean tryRelease(int releases) {
// If the thread trying to unlock is not the one holding the lock, an exception is thrown
if(Thread.currentThread() ! = getExclusiveOwnerThread()) {throw new IllegalMonitorStateException();
}
// Set the thread that currently has exclusive access to NULL
setExclusiveOwnerThread(null);
setState(0);
return true;
}
/** * returns a Condition, each of which contains a Condition queue * for the thread to actively wait and wake up on the specified Condition queue **@returnReturns a new ConditionObject */ each call
Condition newCondition(a) {
return newConditionObject(); }}/** * Simply delegate the action to the Sync instance */
private final Sync sync = new Sync();
/** * lock interface lock method */
@Override
public void lock(a) {
sync.acquire(1);
}
/** * the tryLock method of the lock interface */
@Override
public boolean tryLock(a) {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
@Override
public void lockInterruptibly(a) throws InterruptedException {
sync.acquireInterruptibly(1);
}
/** * Lock interface unlock method */
@Override
public void unlock(a) {
sync.release(1);
}
/** * newCondition of the lock interface */
@Override
public Condition newCondition(a) {
return sync.newCondition();
}
public boolean isLocked(a) {
return sync.isHeldExclusively();
}
public boolean hasQueuedThreads(a) {
returnsync.hasQueuedThreads(); }}Copy the code
If you don’t understand or need to communicate, you can leave a message. In addition, I hope to like, collect, pay attention to, I will continue to update a variety of Java learning blog!