Java concurrent development: Lock framework details, javalock

Source: Internet
Author: User

Java concurrent development: Lock framework details, javalock
Abstract:

We already know that synchronized is a keyword of java and a built-in feature of Java. At the JVM level, it achieves synchronous and mutually exclusive access to critical resources, but synchronized has a large granularity, there are many limitations when dealing with actual problems, such as response interruptions. Lock provides a wider range of Lock operations than synchronized. It can handle thread synchronization problems in a more elegant way. This article takes synchronized and Lock as the starting point, introduces the branches of the Lock framework in Java in detail, and finally provides some related concepts of the Lock.

I. Limitations of synchronized and advantages of Lock

Review the article Java concurrency: built-in lock Synchronized. If a code block is modified by the synchronized keyword, when a thread acquires the corresponding lock and executes the code block, other threads can only wait until the thread that occupies the lock releases the lock. In fact, releasing a lock by a thread that occupies the lock is generally one of the following three situations:

  • The thread that occupies the lock executes the code block and then releases the lock possession;

  • An exception occurs when the thread occupies the lock. At this time, the JVM will allow the thread to automatically release the lock;

  • The occupy lock thread enters the WAITING state to release the lock, for example, calling the wait () method in the thread.

Synchronized is a built-in feature of the Java language that allows you to easily synchronize and mutex access to critical resources. So why does Lock appear? Consider the following three cases:

Case 1:

When the synchronized keyword is used, if the thread occupying the lock is blocked for IO or other reasons (such as calling the sleep method), but the lock is not released, then other threads can only wait. This will greatly affect the program execution efficiency. Therefore, a mechanism is required to prevent the waiting thread from waiting for a long time (for example, only waiting for a certain amount of time (solution: tryLock (long time, TimeUnit unit )) you can also respond to the interruption (solution: lockInterruptibly (). In this case, you can use Lock to solve the problem.

Case 2:

We know that when multiple threads read and write files, read operations and write operations conflict, and write operations also conflict, however, read operations and read operations do not conflict. However, if the synchronized keyword is used for synchronization, a problem occurs. That is, when multiple threads only perform read operations, only one thread can perform read operations, other threads can only wait for the lock to be released and cannot perform read operations. Therefore, a mechanism is required to prevent conflicts between threads when multiple threads only perform read operations. Similarly, Lock can solve this problem (solution: ReentrantReadWriteLock ).

Case 3:

We can know through Lock whether the thread has successfully obtained the Lock (solution: ReentrantLock), but this is not what synchronized can do.

The above three cases can all be solved through Lock, but the synchronized keyword is powerless. In fact, Lock is java. util. concurrent. the locks interface under the locks package provides a wider range of Lock operations than the synchronized keyword. It can handle thread synchronization problems in a more elegant way. That is to say, Lock provides more features than synchronized. Note the following:

1) synchronized is the keyword of Java, so it is a built-in feature of Java and implemented based on JVM. Lock is a Java interface implemented at the JDK level, through which synchronous access can be implemented;

2) When synchronized is used, you do not need to manually release the lock. After the synchronized method or synchronized code block is executed, the system automatically releases the thread to occupy the lock; the Lock requires the user to manually release the Lock. If the Lock is not released, the deadlock may occur.

Ii. Common classes and interfaces in the java. util. concurrent. locks package

The following describes the relationships between common classes and interfaces in the java. util. concurrent. locks package:

1. Lock

By viewing the Lock source code, we can see that Lock is an interface:

Next we will analyze each method in the Lock interface one by one. Lock (), tryLock (), tryLock (long time, TimeUnit unit), and lockInterruptibly () are used to obtain the lock. The unLock () method is used to release the lock. NewCondition () returns the new Condition instance bound to the Lock for inter-thread collaboration. For details, see Java concurrency: Inter-thread communication and collaboration.

1). lock ()

In Lock, the four methods are defined to obtain the Lock. What are the differences between the four methods? First, the lock () method is the most commonly used method, which is used to obtain the lock. If the lock has been obtained by another thread, wait. As mentioned above, if Lock is used, the Lock must be released automatically and will not be automatically released in case of exceptions. Therefore, in general, the use of Lock must be in try... Catch... Block, and put the lock release operation in the finally block to ensure that the lock will be released to prevent deadlock. Lock is usually used for synchronization in the following form:

2). tryLock () & tryLock (long time, TimeUnit unit)

The tryLock () method returns a value, indicating that it is used to obtain the lock. If the acquisition is successful, true is returned. If the acquisition fails (that is, the lock has been obtained by other threads ), false is returned. That is to say, this method will return immediately no matter how it is returned (it will not wait until the lock is obtained ).

The tryLock (long time, TimeUnit unit) method is similar to the tryLock () method, but the difference is that this method will wait for a certain amount of time when the lock is not available, if the lock cannot be obtained within the time limit, false is returned and the service can respond to the interruption. Returns true if the lock is obtained at the beginning or within the waiting period.

Generally, tryLock is used to obtain the lock:

3). lockInterruptibly ()

The lockInterruptibly () method is special. When you use this method to obtain the lock, if the thread is waiting to obtain the lock, the thread can respond to the interruption, that is, the waiting state of the thread to be interrupted. For example, when two threads simultaneously use lock. lockInterruptibly () is used to obtain A lock. If thread A obtains the lock and thread B only waits, thread B calls threadB. the interrupt () method can interrupt the waiting process of thread B.

The lockInterruptibly () Statement throws an exception, so the lock. lockInterruptibly () must be placed in the try block or an InterruptedException is thrown in the external declaration that calls lockInterruptibly (). However, the latter is recommended for reasons described later. Therefore, the general usage of lockInterruptibly () is as follows:

Note that when a thread acquires the lock, it will not be interrupted by the interrupt () method. Because the interrupt () method can only interrupt the thread in the blocking process, but cannot interrupt the thread in the running process. Therefore, when a lock is obtained using the lockInterruptibly () method, if the lock cannot be obtained, the interrupt can be returned only when the lock is waiting. Compared with synchronized, when a thread is waiting for a lock, it cannot be interrupted. It can only wait for a long time.

2. ReentrantLock

ReentrantLock. ReentrantLock is the only class that implements the Lock interface, and ReentrantLock provides more methods. The following examples show how to use ReentrantLock.

Example 1: Use Lock correctly

The results may be surprising. How can the second thread get the lock before the first thread releases the lock? The reason is that the lock variable in the insert method is a local variable, and each thread will save a copy when executing this method, then each thread will execute the lock. different locks are obtained at lock (), so they do not form synchronous and mutually exclusive access to critical resources. Therefore, we only need to declare lock as a member variable, as shown below.

Example 2: tryLock () & tryLock (long time, TimeUnit unit)

Different from tryLock (), tryLock (long time, TimeUnit unit) can respond to an interruption, that is, it supports the interruption of obtaining the lock, however, attempts to obtain an internal lock (entering a synchronized block) cannot be interrupted. As follows:

Example 3: Use lockInterruptibly () to respond to an interruption

After running the above Code, it is found that thread2 can be properly interrupted and the execution of the task is abandoned. Note that if you need to properly interrupt the thread waiting for the lock, you must put the lock to the outside (outside the try statement block) and then throw InterruptedException. If you do not do this, as shown in the following code:

Note that the above Code puts the lock acquisition operation in the try statement block, and the unlock operation in the finally statement block will be executed. After thread B preparing to obtain the lock is interrupted, executing the unlock operation will throw IllegalMonitorStateException, because the thread has not obtained the lock but executed the unlock operation.

3. ReadWriteLock

ReadWriteLock is also an interface in which only two methods are defined:

One is used to obtain the read lock and the other is used to obtain the write lock. That is to say, the read and write operations on critical resources are divided into two locks to be allocated to the thread, so that multiple threads can perform read operations at the same time. The following ReentrantReadWriteLock implements the ReadWriteLock interface.

4. ReentrantReadWriteLock

ReentrantReadWriteLock provides many methods, but there are two main methods: readLock () and writeLock () to obtain the Read and Write locks. The following example shows how to use ReentrantReadWriteLock. If multiple threads need to perform read operations at the same time, let's take a look at the effects of synchronized:

The output result of this program is that the read operation information of thread B is printed only after thread A executes the read operation. Instead, use the read/write lock:

We can see that thread A and thread B perform read operations at the same time, which greatly improves the read operation efficiency. Note that if one thread has occupied the read lock, other threads will wait until the read lock is released if they want to apply for the write lock. If one thread occupies the write lock, if other threads apply for the write lock or read lock, the applied thread will remain waiting to release the write lock.

5. Selection of Lock and synchronized

In general, Lock and synchronized have the following differences:

  • (1) Lock is an interface and JDK-level implementation. synchronized is a keyword in Java, a built-in feature of Java, and a JVM-level implementation;

  • (2) When a synchronized exception occurs, the Lock occupied by the thread is automatically released, so the deadlock will not occur. When a Lock exception occurs, if it does not actively pass unLock () to release the Lock, it is likely to cause a deadlock. Therefore, you must release the Lock in the finally block when using the Lock;

  • (3) Lock can interrupt the response of the thread waiting for the Lock. When synchronized is used, the waiting thread will keep waiting and cannot respond to the interruption;

  • (4) You can know whether the Lock is successfully obtained through Lock, but synchronized cannot;

  • (5) Lock can improve the reading efficiency of multiple threads.

In terms of performance, if the competition resources are not fierce, the performance of the two is similar. When the competing resources are very fierce (that is, there are a large number of threads competing at the same time), the Lock performance is far better than synchronized. Therefore, you must select an appropriate option for specific use.

Iii. Introduction to lock-related concepts

1. reentrant locks

If the lock is reentrant, it is called a reentrant lock. For example, synchronized and ReentrantLock are both reentrant locks. In my opinion, they actually indicate the lock allocation mechanism: thread-based allocation rather than method-based allocation. For example, when a thread executes a synchronized method, such as method1, and method1 calls another synchronized Method method2, the thread does not have to apply for a lock again, instead, you can directly execute the method method2.

The two methods method1 and method2 in the above Code are modified with synchronized. If thread A executes method1 at A certain time, thread A obtains the Lock of this object, and method2 is also the synchronized method, if synchronized is not reentrant, in this case, thread A needs to apply for A lock again. However, this will cause A deadlock because thread A already holds the lock for this object and is applying to obtain the lock for this object, in this way, thread A waits for the lock that will never be obtained. Because both synchronized and Lock are reentrant, the above phenomenon will not occur.

2. locks that can be interrupted

As the name suggests, an interruption lock is a lock that can respond to an interruption. In Java, synchronized is not an interruption Lock, but a Lock is an interruption Lock.
If thread A is executing the lock code and thread B is waiting to obtain the lock, thread B may not want to wait because the wait time is too long and want to handle other tasks first, we can let it interrupt itself or interrupt it in other threads. This is the locks that can be interrupted. The usage of tryLock (long time, TimeUnit unit) and lockInterruptibly () has been demonstrated in the previous section.

3. Fair lock

Fair locks are obtained in the order of request locks. For example, multiple threads are waiting for a lock. When the lock is released, the thread with the longest wait time (the thread with the first request) will obtain the lock. This is the fair lock. Non-fair locks do not guarantee that the locks are obtained in the order of request locks, which may cause some or some threads to never obtain the locks.

In Java, synchronized is an unfair lock, which cannot guarantee the order in which the waiting thread obtains the lock. For ReentrantLock and ReentrantReadWriteLock, it is a non-fair lock by default, but can be set as a fair lock.

Let's look at the following two examples:

Case: fair lock

Case: unfair lock

Based on the demo results of the above code, we can see that (the more threads, the more obvious), in the case of fair lock, multiple threads are waiting for a lock. Generally, the thread with the longest wait time (the thread with the first request) will obtain the lock. In the case of unfair locks, it cannot be ensured that the locks are obtained in the order of request locks.

In addition, many methods are defined in the ReentrantLock class. For example:

  • IsFair () // checks whether the lock is a fair lock.

  • IsLocked () // determine whether the lock is obtained by any thread

  • IsHeldByCurrentThread () // checks whether the lock is obtained by the current thread.

  • HasQueuedThreads () // determines whether a thread is waiting for the lock.

  • GetHoldCount () // queries the number of times the current thread occupies the lock

  • GetQueueLength () // gets the number of threads waiting for this lock

  • GetWaitQueueLength (Condition condition) // The number of threads that are waiting for the condition Condition related to this lock has a similar method in ReentrantReadWriteLock, and can also be set as a fair lock and an unfair lock. Remember, ReentrantReadWriteLock does not implement the Lock interface, but it implements the ReadWriteLock interface.

4. read/write lock

The read/write lock divides access to critical resources into two locks: one read lock and one write lock. Because of the read/write lock, the read operations between multiple threads do not conflict. ReadWriteLock is the read/write lock, which is an interface that is implemented by ReentrantReadWriteLock. Read locks can be obtained through readLock () and write locks can be obtained through writeLock. The usage of read/write locks has been demonstrated in the previous section.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.