Java concurrency core framework Lock interface

introduction

Finally got to this point. Before JDK5, Java used synchronizedkeywords to realize the synchronization lock function. Through the previous description of synchronizedkeywords, I believe we have a good understanding of its characteristics. It is to ensure the synchronization of multi-threaded concurrency Thread safety of access to shared resources. Starting from JDK5, Java provides another way of locking, that is , the Lock display lock that we mentioned in the Visibility, Ordering and Atomicity and Non-Atomic Agreement chapters. Lock display locks are also the main reason for the existence of Java concurrent packages. It provides thread synchronization functions similar to the synchronized keyword, and using Lock to display locks also satisfies the happens-before principle. Lock shows that the lock and synchronized keywords support the thread synchronization solution at the Java language level. Both have their own advantages and disadvantages and are suitable for different scenarios.

 

Lock interface

Lock is the basic core component of Java concurrent packages, but it is only an interface, because the AbstractQueuedSynchronizer implemented in a template is the lowest and most refined implementation of the display lock Lock, but Lock lock is used as an upper-level synchronizer for different scenarios. The locking feature must have different requirements in different scenarios, so in order to achieve the locking feature in different scenarios, it is necessary to use the underlying implementation of AQS as the cornerstone, and achieve different feature needs by implementing its specific template method (for example: reentrancy, fairness, read-write locks, etc.). After talking for a long time, isn't this our approach to "custom AQS synchronizer" in the continuation chapter of synchronous blocking and awakening, one of the core frameworks of Java concurrent packets, AQS .

That's right, the custom synchronizer is to construct the Lock display lock that meets different scenarios. Remember the first thing we need to do when we "customize the synchronizer", that is, we need to define an external interface first, and use the This interface shields the specific implementation of the Lock lock, and only provides a few simple operation methods for the Lock lock. Since every time you customize a synchronizer, you need to define an external interface first, and Java concurrent packages, as JDK, need to provide developers with the implementation of several of the most common and general display locks, so a general display lock interface is naturally born. Now, when implementing a custom synchronizer, we can directly implement this interface. This general interface is the Lock interface we are talking about here.

 

Let's first look at the interface methods provided by the Lock interface that need to be implemented by developers:


 It can be seen from the methods of the Lock interface that, except for the blocking method of acquiring locks, void lock(), which is almost identical to the language of the synchronized keyword, the other methods of acquiring locks cannot be provided by the synchronized keyword. This is where Lock shows that locks are more powerful than the synchronized keyword. 

 

Use of Lock interface

Lock lock = new ReentrantLock(); //Take ReentrantLock as an example
	lock.lock();//The lock acquisition operation cannot be placed in the try block, because if an exception occurs when acquiring the lock, it will also cause the lock to be released for no reason
	try {
		// thread-safe operations may occur
	} finally {
		// must release the lock in finally
		// Also can't acquire the lock in try, because it is possible to throw an exception when acquiring the lock
		lock.unlock();
	}

   When using the Lock display lock, there are two points to pay attention to most: 1. Do not put the operation of acquiring the lock into the try block; 2. Be sure to remember to execute unlock() in the finally block to release the lock, otherwise it may lead to death Lock.

 

The difference between Lock display lock and synchronize implicit lock

Implementation difference :

synchronize is a platform-related native code implementation written in C/C++. The bottom layer uses CAS to operate the Mark Word in the object header of the lock object to identify add/unlock. More details about the lock are included in the Mark Word. The object to which the pointer points (Lock Record, Thread ID or ObjectMonitor). When implementing heavyweight locks, the JVM implements a more refined implementation, dividing the waiting queue into ContentionList and EntryList, in order to reduce the thread dequeuing speed. In addition, a _WaitSet queue is used to store threads waiting for conditions.

The Lock display lock is implemented by the AbstractQueuedSynchronizer queue synchronizer written in Java. It uses CAS internally to operate a volatile-modified state variable to identify adding/unlocking, and uses a doubly linked list to store blocked threads waiting for synchronization. For different Conditional waiting for blocking threads is also stored in different doubly linked lists.

In terms of implementation, Lock shows that the lock may not be better than synchronize, because synchronize is implemented directly using native code. After all, the efficiency of JAVA language and native language are always worse than most cases, and synchronize implements spin lock and targets at Different systems and hardware architectures are optimized for locks.

Differences in operating mechanism:

synchronize is an exclusive pessimistic lock, why do you say synchronize is a pessimistic lock? This should mainly refer to the case of heavyweight locks, because before entering the synchronize block, heavyweight locks are pessimistic that the data has been changed, and the corresponding lock must be acquired first, regardless of whether there is multi-threaded competition at this time. condition.

When the Lock lock implements the locking mechanism, it uses CAS, and it is assumed that there is no competition, that is, it is assumed that the synchronization resource state variable has not been modified, and the locking will only be performed after the CAS operation fails. In the implementation of write locks, even more fine-grained lock condition judgments are made.

Functional difference:

The synchronize implicit lock is completely scheduled by the JVM for an exclusive lock, so the function is more single. The implementation of thread communication is also relatively simple, and only one conditional wait()/notify() mechanism can be used.

The Lock display lock provides a non-blocking and interrupt-responsive locking implementation, and can also implement read-write locks, shared locks, public locks, unfair locks, etc. suitable for different scenarios as needed. more flexible. Facing complex business logic is more handy. It is also more powerful in the implementation of thread communication, and you can use binding multiple Condition instances to obtain different conditional waits. More flexible than wait()/notify().

Differences in usage:

Using synchronize is simpler, and the process of acquiring and releasing locks is done by the JVM itself, making it easier for developers to use it with confidence.

Using Lock to reveal the lock, the developer must acquire the lock manually and cannot forget to release the lock in the finally block, otherwise a deadlock will occur.

Differences in log analysis:

It is said that using synchronize is more clear when it is necessary to analyze thread dump data search problems such as deadlocks, while using Lock to display locks is not so convenient, but I have not personally verified it, but it is said that there is no thread in LockSupport that can be set. Who is blocking the method setBlocker?

 

Lock shows the usage scenarios of the lock

It does not mean that after the Lock lock appears, we should replace all the locking methods using synchronize for no reason. Only in the following situations should we consider using the Lock display, otherwise we should use the synchronize keyword. , after all it is simpler and well optimized, and its performance may even be better in the future.

1. Lock There is a clear need for a certain advanced feature, such as response timeout or interrupt, or non-blocking or even need to implement a specific synchronizer, or thread communication in wait/notify mode is inconvenient (such as multi-condition judgment).

2. There is clear evidence (rather than just suspicion) that synchronization has become a scalability bottleneck in specific cases.

3. Under the condition of high multi-threading competition, you can choose to use Lock to display the implementation of the lock ReentrantLock , because it performs better in high contention.

 

Why be conservative when using Lock display locks, because synchronized still has some advantages for Lock display locks. For example, when using synchronized, it's impossible to forget to release the lock; the  synchronized JVM does it for you when you exit the block. You can easily forget to  finally release the lock with a block, which is very bad for your program. On the other hand almost every developer is familiar with synchronized, but not every developer shows the use of the lock property Lock. This makes code maintenance easier.

 

To sum up , the Lock framework is a compatible alternative to synchronization, it provides  synchronized many features that it does not provide, and its implementation provides better performance under high contention. But it is not enough to replace it completely synchronized . We should make a judgment on whether to use the Lock display lock according to our needs. In most cases, we still give priority to using synchronized it, and only use it when we really need  Lock it.

 

Generic implementation of Lock display lock

In JDK8, Java mainly implements the following display locks through the Lock interface: ReentrantLock, ReadLock, ReadLockView, WriteLock, WriteLockView. Among them, ReentrantLock is synchronized the implementation of the corresponding Java version of the lock, and ReadLock and WriteLock are the internal basis for implementing ReentrantReadWriteLock read-write lock, ReadLockView and WriteLockView are the internal basis for implementing StampedLock lock, and ReentrantLock is even the inheritance parent class that implements ConcurrentHashMap internal lock Segment . In the following chapters, we will understand and analyze the implementation classes of these Lock interfaces one by one and the characteristics of these display locks.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326024667&siteId=291194637