preface

About multi-threading knowledge summarized an atlas, to share with you:

AQS was designed to be

For example, there is a scenario where four threads need to occupy a resource at the same time due to business requirements, but the resource can only be occupied by one thread at a time. So the question is, how do you signal that the resource is occupied? If reentrant is supported, how to identify the number of rushes? What about the design of threads that compete for resources? Is it fair or unfair? How do I determine if the last thread has released resources?

That’s what AQS is designed for. AQS is a synchronization framework that integrates synchronization state management, thread blocking, thread release and queue management. The core idea is that when multiple threads compete for resources, the threads that fail to compete for resources are constructed as Node nodes and placed in a two-way FIFO queue. Threads put into the queue remain blocked until they are woken up by the precursor node.

Resource occupancy identifier, reentrant

There is such a variable AbstractQueuedSynchronizer

/** * The synchronization state. */
private volatile int state;
Copy the code

And a bunch of methods below

/**
 * Returns the current value of synchronization state.
 * This operation has memory semantics of a {@code volatile} read.
 * @return current state value
 */
protected final int getState() {
	return state;
}
/**
 * Sets the value of synchronization state.
 * This operation has memory semantics of a {@code volatile} write.
 * @param newState the new state value
 */
protected final void setState(int newState) {
	state = newState;
}
/**
 * Atomically sets synchronization state to the given updated
 * value if the current state value equals the expected value.
 * This operation has memory semantics of a {@code volatile} read
 * and write.
 *
 * @param expect the expected value
 * @param update the new value
 * @return {@code true} if successful. False return indicates that the actual
 *         value was not equal to the expected value.
 */
protected final Boolean compareAndSetState(int expect, int update) {
	return U.compareAndSwapint(this, STATE, expect, update);
}
Copy the code

The synchronization state in AQS is managed by a state variable of type int

1. State =0 The resource is not used by the thread

2. State =1 The resource is used by the thread

3. State =N (N>1) The resource is reentered by a thread for N times

With state, we can easily identify the state of a resource and the number of threads reentrant

Design patterns

On the implementation, recommend a subclass is defined as the custom synchronous components of the static inner class, AQS itself does not implement any synchronization interfaces, it is simply defines the number of state synchronization acquisition and release methods for use by the custom synchronous components, synchronizer can support to exclusive access to sync, can also support to Shared access to sync, This makes it easy to implement different types of synchronization components (ReentrantLock, ReentrantReadWriteLock, CountDownLatch, etc.).

Synchronizers are key to implementing locks (which can be any synchronization component) and are aggregated in the implementation of locks. The relationship between the two can be understood as follows:

1. The lock is user-oriented. It defines the interface for the user to interact with the lock (for example, it can allow two threads to access it in parallel), and hides the implementation details;

2. The synchronizer is for the implementor of the lock, which simplifies the implementation of the lock and shields the synchronization state management, queuing, waiting and wake up of the thread and other low-level operations.

Locks and synchronizers do a good job of isolating areas of concern for consumers and implementers. The implementer inherits the synchronizer and overwrites the specified methods, then combines the synchronizers in the implementation of the custom synchronization component and calls the template methods provided by the synchronizer that will invoke the user-overridden methods.

Template method Pattern Reference article: Template method pattern

Multithreading

Review the above question:

1. In what order do threads fetch resources?

2. How do other threads know when the resource usage ends?

The basic idea of AQS – CLH queue locking

CLH queue locks are also scalable, high-performance, and fair spinlocks based on linked lists. The requisition thread spins only on local variables. It continuously polls the state of the precursor and terminates the spin if it finds that the precursor releases the lock

CLH lock: Craig, Landin, and Hagersten (CLH) lock. CLH lock: Craig, Landin, and Hagersten (CLH) lock.

Its structure is as follows:

Explain the structure:

1. Node QNode consists of Pointers, threads, and locked

1.1 Pointer to myPre of the previous QNode

1.2 Locked =true Resources need to be obtained and are not released. False indicates that locks are released

2. Queue composed of linked lists, first in, first out. Each new thread or thread that has released resources is wrapped as a QNode and inserted at the end of the queue (fairly locked) with a pointer to the myPre of the precursor node

When a thread wants to acquire a lock

1. Create a QNode, set locked to true to obtain locks, and myPred to reference its precursor

2. Thread A calls the getAndSet method on the tail domain, making itself the tail of the queue and getting A reference to its precursor, myPred; Thread B needs to acquire the lock, and the same process starts again

3. The thread rotates on the locked fields of the forelock until the forelock is released (locked == false).

4. When a thread needs to release a lock, set the locked field of the current node to false and reclaim the precursor node. Thread A obtains the lock when the locked field of thread A’s myPred is false

The optimization of AQS

The basic idea of AQS is CLH, which has been optimized in concrete implementation

1.0AQS is not a single linked list, but a bidirectional linked list

2. Spin does not spin many times, usually two or three times, two failures in the current thread into the block

A reentrant chestnut

*/ SelfZLock implements Lock {// SelfZLock implements Lock {// SelfZLock implements Lock Extends AbstractQueuedSynchronizer {/ * * * is monopolistic state * @ return * / @ Override protected Boolean isHeldExclusively () {return getState() > 0; } @override protected Boolean tryAcquire(int arg) {if (compareAndSetState(0, 0, 0); 1)) { setExclusiveOwnerThread(Thread.currentThread()); return true; } else if (getExclusiveOwnerThread() = thread.currentThread ()) {// getState() + 1; return true; } return false; } /** * release lock, @param arg * @return */ @override protected Boolean tryRelease(int arg) {if (getExclusiveOwnerThread()! = Thread.currentThread()) { throw new IllegalMonitorStateException(); } if (getState() == 0) { throw new IllegalMonitorStateException(); } setState(getState() - 1); if (getState() == 0) { setExclusiveOwnerThread(null); } return true; / / Condition newCondition() {return new ConditionObject(); / / Condition newCondition() {return new ConditionObject(); } } private final Sync sync = new Sync(); @Override public void lock() { System.out.println(Thread.currentThread().getName()+" ready get lock"); sync.acquire(1); System.out.println(Thread.currentThread().getName()+" already got lock"); } @Override public Boolean tryLock() { return sync.tryAcquire(1); } @Override public void unlock() { System.out.println(Thread.currentThread().getName()+" ready release lock"); sync.release(1); System.out.println(Thread.currentThread().getName()+" already released lock"); } @Override public Condition newCondition() { return sync.newCondition(); } public Boolean isLocked(){ return sync.isHeldExclusively(); } public Boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } public Boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); }}Copy the code

The last

I here organized a Java multithreading information document, Spring series of family barrel, Java systematic information :(including Java core knowledge, interview topics and 20 years of the latest Internet real questions, e-books, etc.) have the need of friends can pay attention to the public number [procedures yuan small wan] can be obtained.