Lock is a top-level interface, and all its methods are shown in the following figure:
Its subclass list is as follows:
We usually use ReentrantLock to define its instances. The relationship between them is as shown in the figure below:
PS: Sync means synchronization lock, FairSync means fair lock, and NonfairSync means unfair lock.
Learning any skill starts with using it, so we are no exception. Let’s first take a look at the basic use of ReentrantLock:
public class LockExample { // 创建锁对象 private final ReentrantLock lock = new ReentrantLock(); public void method() { // 加锁操作 lock.lock(); try { // 业务代码...... } finally { // 释放锁 lock.unlock(); } } }
After ReentrantLock is created, there are two key operations:
Lock operation: lock()
Release lock operation: unlock()
For example, as follows This code:
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { // 定义线程任务 Runnable runnable = new Runnable() { @Override public void run() { // 加锁 lock.lock(); try { // 打印执行线程的名字 System.out.println("线程:" + Thread.currentThread().getName()); } finally { // 释放锁 lock.unlock(); } } }; // 创建多个线程 for (int i = 0; i < 10; i++) { new Thread(runnable).start(); } } }
The execution results of the above program are as follows:
It can be seen from the above execution results , ReentrantLock is an unfair lock by default. Because the names of threads are incremented according to the order in which they are created, if it is a fair lock, then the execution of the threads should be incremented in order. However, as can be seen from the above results, the execution and printing of the threads are out of order. This Description ReentrantLock is an unfair lock by default. It is very simple to set ReentrantLock as a fair lock. You only need to set a true construction parameter when creating ReentrantLock.The following code is shown:
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象(公平锁) private static final ReentrantLock lock = new ReentrantLock(true); public static void main(String[] args) { // 定义线程任务 Runnable runnable = new Runnable() { @Override public void run() { // 加锁 lock.lock(); try { // 打印执行线程的名字 System.out.println("线程:" + Thread.currentThread().getName()); } finally { // 释放锁 lock.unlock(); } } }; // 创建多个线程 for (int i = 0; i < 10; i++) { new Thread(runnable).start(); } } }
The execution results of the above program are as follows:
As can be seen from the above results, when we explicitly set the true constructor for ReentrantLock After passing the parameters, ReentrantLock becomes a fair lock, and the order in which threads acquire locks becomes orderly. In fact, from the source code of ReentrantLock, we can also see whether it is a fair lock or an unfair lock.Part of the source code of ReentrantLock is implemented as follows:
public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }From the above source code, you can It can be seen that ReentrantLock will create an unfair lock by default. If the value of the construction parameter is explicitly set to true during creation, it will create a fair lock. 2. Release the lock in finally
Be sure to remember to release the lock when using ReentrantLock, otherwise the lock will be occupied forever, and other threads using the lock will be permanently occupied. Waiting , so when we use ReentrantLock, we must release the lock in finally, so as to ensure that the lock will be released.
Counterexample
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { // 加锁操作 lock.lock(); System.out.println("Hello,ReentrantLock."); // 此处会报异常,导致锁不能正常释放 int number = 1 / 0; // 释放锁 lock.unlock(); System.out.println("锁释放成功!"); } }
The execution results of the above program are as follows:
## From the above It can be seen from the results that when an exception occurs, the lock is not released normally, which will cause other threads using the lock to be in a waiting state permanently.
Positive exampleimport java.util.concurrent.locks.ReentrantLock;
public class LockExample {
// 创建锁对象
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
// 加锁操作
lock.lock();
try {
System.out.println("Hello,ReentrantLock.");
// 此处会报异常
int number = 1 / 0;
} finally {
// 释放锁
lock.unlock();
System.out.println("锁释放成功!");
}
}
}
From It can be seen from the above results that although an exception occurs in the method, it does not affect the release operation of the ReentrantLock lock, so that other threads using this lock can obtain and run normally.
3. The lock cannot be released multiple times
One lock corresponds to two unlock operations, causing the program to report an error and terminate execution. The sample code is as follows:
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
// 创建锁对象
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
// 加锁操作
lock.lock();
// 第一次释放锁
try {
System.out.println("执行业务 1~");
// 业务代码 1......
} finally {
// 释放锁
lock.unlock();
System.out.println("锁释锁");
}
// 第二次释放锁
try {
System.out.println("执行业务 2~");
// 业务代码 2......
} finally {
// 释放锁
lock.unlock();
System.out.println("锁释锁");
}
// 最后的打印操作
System.out.println("程序执行完成.");
}
}
# As can be seen from the above results, when the second unlock is executed, the program reports an error and terminates execution. , causing the code after the exception to not execute normally.
在使用 ReentrantLock 时,需要注意不要将加锁操作放在 try 代码中,这样会导致未加锁成功就执行了释放锁的操作,从而导致程序执行异常。
反例
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { try { // 此处异常 int num = 1 / 0; // 加锁操作 lock.lock(); } finally { // 释放锁 lock.unlock(); System.out.println("锁释锁"); } System.out.println("程序执行完成."); } }
以上程序的执行结果如下:
从上述结果可以看出,如果将加锁操作放在 try 代码中,可能会导致两个问题:
未加锁成功就执行了释放锁的操作,从而导致了新的异常;
释放锁的异常会覆盖程序原有的异常,从而增加了排查问题的难度。
The above is the detailed content of What are the common pitfalls of ReentrantLock in Java?. For more information, please follow other related articles on the PHP Chinese website!