Example code of ReentrantLock reentrant lock for Java concurrent programming
- 2021-08-16 23:47:52
- OfStack
Directory 1. ReentrantLock Reentrant Lock Overview 2. Reentrant 3. Interruptible 4. Lock Timeout 5. Fair Lock 6. Condition Variable Condition
1. ReentrantLock reentrant lock overview
Compared with synchronized, it has the following characteristics
Interruptible
The synchronized lock cannot be interrupted when added, the a thread applies the lock, and the b thread cannot cancel it
You can set a timeout
When synchronized goes to acquire the lock, if the other party holds the lock, it will enter entryList1 and wait straight. The reentrant lock can set a timeout time, and if the lock cannot be acquired within the specified time, the lock will be abandoned
Can be set to a fair lock
Prevent thread starvation, that is, first come, first served. If there are more people competing for it, it may happen that you will never get a lock
Supports multiple condition variables and multiple waitset (does not support condition 1 de-a does not support condition 2 de-b)
synchronized only supports the same waitset.
Like synchronized 1, both support reentrant
Basic grammar
// Acquisition lock
reentrantLock.lock();
try {
// Critical region
} finally {
// Release lock
reentrantLock.unlock();
}
synchronized protects critical sections at the keyword level, while reentrantLock protects critical sections at the object level. The critical section is the code that accesses the shared resource. In finally, it is indicated that the lock will be released regardless of whether an exception occurs in the future, and releasing the lock calls the unlock method. Otherwise, the lock cannot be released, and other threads will never acquire the lock.
2. Reentrant
Reentrant means that if the same thread acquires the lock for the first time, it has the right to acquire the lock again because it is the owner of the lock
If it is a non-reentrant lock, you will be blocked by the lock when you get the lock for the second time
ReentrantLock and synchronized are both reentrant locks.
public class TestReentranLock1 {
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
method1();
}
public static void method1() {
lock.lock();
try {
System.out.println("execute method1");
method2();
} finally {
lock.unlock();
}
}
public static void method2() {
lock.lock();
try {
System.out.println("execute method2");
method3();
} finally {
lock.unlock();
}
}
public static void method3() {
lock.lock();
try {
System.out.println("execute method3");
} finally {
lock.unlock();
}
}
}
execute method1
execute method2
execute method3
3. Interruptible
Interruptible means that while waiting for a lock, other threads can use the interrupt method to terminate my wait. The synchronized lock is non-interruptible.
If we want to be interrupted while waiting for a lock, we need to lock the lock object using the lockInterruptibly () method instead of the lock () method
public class TestReentranLock2 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
try {
// If there is no competition, this method gets the lock Lock of object
// If there is a race, it enters the blocking queue and waits, which can be used by other threads interrupt Interrupt
System.out.println(" Try to acquire a lock ");
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(" Was interrupted in the process of waiting for the lock ");
return;
}
try {
System.out.println("t1 The lock was acquired ");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
System.out.println(" The main thread acquired the lock ");
t1.start();
try {
try {
sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.interrupt();
System.out.println(" Execution interrupt t1");
} finally {
lock.unlock();
}
}
}
The main thread acquired the lock
Try to acquire a lock
Execution interrupt t1
Was interrupted in the process of waiting for the lock
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at cn.yj.jvm.TestReentranLock2.lambda$main$0(TestReentranLock2.java:15)
at java.lang.Thread.run(Thread.java:748)
Note that if it is non-interruptible mode, the wait will not be interrupted even if interrupt is used, that is, it is not. That is, the lock () method is used.
This method can avoid deadlock and endless waiting.
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
System.out.println(" Start ...");
lock.lock();
try {
System.out.println(" The lock was acquired ");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
System.out.println(" The lock was acquired ");
t1.start();
try {
sleep(1);
t1.interrupt();
System.out.println(" Execution interrupt ");
sleep(1);
} finally {
System.out.println(" Lock released ");
lock.unlock();
}
4. Lock timeout
ReentranLock supports interruptible, in fact, in order to avoid dead waiting, which can reduce the occurrence of deadlock. In fact, interruptible this way belongs to a kind of passive avoidance of death, which is interrupted by other threads interrupt.
Lock timeout is an active way to avoid waiting.
The lock is acquired using the tryLock () method, that is, it tries to acquire the lock, if it succeeds, it acquires the lock, and if it fails, it can wait without entering the blocking queue, and it will return false, indicating that it did not acquire the lock.
Fail at once
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
System.out.println(" Start ...");
if (!lock.tryLock()) {
System.out.println(" Unable to acquire the lock, fail immediately and return ");
return;
}
try {
System.out.println(" The lock was acquired ");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
System.out.println(" The lock was acquired ");
t1.start();
try {
try {
sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
The lock was acquired
Start...
Unable to acquire the lock, fail immediately and return
Timeout failure
lock. tryLock (1, TimeUnit. SECONDS) indicates an attempt to wait for 1s. If the main thread does not release the lock, it returns false, and if it releases the lock, it returns true. tryLock also supports interruptions, which report exceptions.
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug(" Start ...");
try {
if (!lock.tryLock(1, TimeUnit.SECONDS)) {
log.debug(" Get wait 1s Failure after, return ");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
log.debug(" The lock was acquired ");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
log.debug(" The lock was acquired ");
t1.start();
try {
sleep(2);
} finally {
lock.unlock();
}
Output
18:19:40. 537 [main] c. TestTimeout-Lock obtained
18:19:40. 544 [t1] c. TestTimeout-Start...
18:19:41. 547 [t1] c. TestTimeout-Failed after waiting for 1s and returned
5. Fairness Lock
For synchronized, it is an unfair lock. When one thread holds the lock, other threads will enter the blocking queue and wait. When the lock holder releases the lock, these threads will rush up. Whoever grabs it first will become the owner of monitor, instead of following the first-come, first-served rule.
ReentrantLock default is unfair
ReentrantLock has a parametric construction method. It is unfair to acquiesce.
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
We can guarantee its fairness by changing Boolean values to true. Threads in the impending blocking queue will be executed in the order of entering the blocking queue when competing for locks, on a first-come-first-served basis.
6. Conditional variable Condition
There are also conditional variables in synchronized, that is, the waitSet lounge when we talk about the principle. When the conditions are not met, enter waitSet and wait
The condition variable of ReentrantLock is stronger than that of synchronized in that it supports multiple condition variables, which is like
synchronized is a message that those threads that do not meet the conditions are all in a lounge and so on
ReentrantLock supports multiple lounges, including a lounge waiting for cigarettes and a lounge waiting for breakfast. When you wake up, you also wake up according to the lounge
Key points for use:
Need to acquire lock before await After await executes, it releases the lock and enters conditionObject to wait The thread of await is awakened (or interrupted, or timed out) to re-compete for lock lock After the lock lock is successfully competed, continue to execute after await signal is equivalent to notify, and signalAll is equivalent to notifyAll
static ReentrantLock lock = new ReentrantLock();
static Condition waitCigaretteQueue = lock.newCondition();
static Condition waitbreakfastQueue = lock.newCondition();
static volatile boolean hasCigrette = false;
static volatile boolean hasBreakfast = false;
public static void main(String[] args) {
new Thread(() -> {
try {
lock.lock();
while (!hasCigrette) {
try {
waitCigaretteQueue.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug(" When it comes to its smoke, ");
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
try {
lock.lock();
while (!hasBreakfast) {
try {
waitbreakfastQueue.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug(" When it comes to its breakfast, ");
} finally {
lock.unlock();
}
}).start();
sleep(1);
sendBreakfast();
sleep(1);
sendCigarette();
}
private static void sendCigarette() {
lock.lock();
try {
log.debug(" Here comes the smoke ");
hasCigrette = true;
waitCigaretteQueue.signal();
} finally {
lock.unlock();
}
}
private static void sendBreakfast() {
lock.lock();
try {
log.debug(" Here comes breakfast ");
hasBreakfast = true;
waitbreakfastQueue.signal();
} finally {
lock.unlock();
}
}
Output
18:52:27. 680 [main] c. TestCondition-Here comes breakfast
18:52:27. 682 [Thread-1] c. TestCondition-Wait for its breakfast
18: 52: 28. 683 [main] c. TestCondition-Here comes the smoke
18:52: 28.683 [Thread-0] c. TestCondition-Wait for its smoke