1. Preface
- This article is a follow-up to the previous article. The case is the same case. It is very simple. It is the case of taking the order number when going to teacher Tian for dinner. Without too much introduction, you can also see the detailed introduction of the previous article, as follows:
1.1 synchronized solves thread safety problems
Examples of thread safety problems and solutions (synchronized).
2. Examples of thread safety problems and Solutions
- wait for
2.1 existence of thread safe code
- For convenience, I've removed the "T" here and directly use numbers. That's not the focus of our demonstration. Let's look at the code directly:
package com.liu.susu.thread.lock.example3;
/**
* @FileName OrderNumberRun
* @Description Start three threads to generate the order number
* @Author susu
* @date 2022-03-03
**/
public class OrderNumberRun implements Runnable {
private int number = 1;//The pick-up order number in line starts from the 1st
@Override
public void run() {
//Suppose there is a discount for the top 100 orders today
while (true) {
if (number <= 100) {
System.out.println(Thread.currentThread().getName() + "-Mr. Tian's house, take the order number as-->" + number);
number++;
}else {
break;
}
}
}
}
class clientTest3 {
public static void main(String[] args) {
OrderNumberRun orderNumberRun = new OrderNumberRun();
Thread thread1 = new Thread(orderNumberRun);
Thread thread2 = new Thread(orderNumberRun);
Thread thread3 = new Thread(orderNumberRun);
thread1.setName("Thread 1");
thread2.setName("Thread 2");
thread3.setName("Thread 3");
thread1.start();
thread2.start();
thread3.start();
}
}
- Effect display:
It's obviously unsafe. It's also introduced in the previous article. I won't say much. Let's focus on the treatment scheme2.2 solving thread safety with ReentrantLock
- The modified code is shown in the figure below:
- The complete code is as follows:
package com.liu.susu.thread.lock.example4;
import java.util.concurrent.locks.ReentrantLock;
/**
* @FileName OrderNumberRun
* @Description Start three threads to generate the order number
* @Author susu
* @date 2022-03-03
**/
public class OrderNumberRun implements Runnable {
private int number = 1;//The pick-up order number in line starts from the 1st
/**
* 1.Instantiate ReentrantLock to define the lock
* If the parameter fair is set to true -- > fair lock (that is, three threads take the lock fairly to prevent one thread from taking out the order number), the default fair is false
*/
private final ReentrantLock lock = new ReentrantLock(true);
@Override
public void run() {
//Suppose there is a discount for the top 100 orders today
while (true) {
try {
//2. Call the lock() method to lock
lock.lock();
if (number <= 100) {
System.out.println(Thread.currentThread().getName() + "-Mr. Tian's house, take the order number as-->" + number);
number++;
}else {
break;
}
} finally {
//3. Call the unlock() method to unlock
lock.unlock();
}
}
}
}
class clientTest4 {
public static void main(String[] args) {
OrderNumberRun orderNumberRun = new OrderNumberRun();
Thread thread1 = new Thread(orderNumberRun);
Thread thread2 = new Thread(orderNumberRun);
Thread thread3 = new Thread(orderNumberRun);
thread1.setName("Thread 1");
thread2.setName("Thread 2");
thread3.setName("Thread 3");
thread1.start();
thread2.start();
thread3.start();
}
}
- The operation effect is as follows:
3. The difference between synchronized and ReentrantLock
3.1 explanation
3.1.1 similarities
- It is certain that both synchronized and ReentrantLock can solve thread safety problems
- ReentrantLock refers to a reentrant lock. Like synchronized, it belongs to a reentrant lock;
Reentrant lock: if the same thread obtains the lock resource for the first time, it has the right to obtain the lock again. This is the reentry of the lock! - Both are locked synchronization and blocking synchronization, that is, if a thread obtains an object lock and enters the synchronization block, other threads accessing the synchronization block must block and wait outside the synchronization block, The cost of thread blocking and wake-up is relatively high (the operating system needs to switch back and forth between user state and kernel state, which is very expensive, but it can be improved by lock optimization).
3.1.1 differences
(1) Implementation difference
- Use lock initialization to select fair locks and non fair locks
- Lock lock only has code block lock, and synchronized has code block lock and method lock
- synchronized is an implicit lock that automatically releases the synchronization monitor (automatically releases the lock)
Lock is a display lock. You need to manually start synchronization (lock() method locks), and you need to manually unlock it when you end synchronization (call the unlock () method) - For Synchronized, it is a keyword of the java language and a mutual exclusion at the native syntax level, which needs to be implemented by the JVM. ReentrantLock is jdk5 The mutex at the API level provided after 0 needs the lock() and unlock() methods together with the try/finally statement block to complete
- Synchronized is more convenient to use, and the compiler ensures the locking and release of locks. ReenTrantLock needs to declare manually to add and release locks. In order to avoid deadlock caused by forgetting to release locks manually, it is best to declare the release of locks in finally.
(2) Performance difference
- Using lock lock, the JVM will spend less time to schedule threads, with better performance and good scalability (providing more subclasses)
3.2 table comparison
contrast | synchronized | ReentrantLock |
---|
appear | Always | JDK5.0 NEW |
Reentrancy | Reentrant | Reentrant |
Lock type | Fair lock / unfair lock | Unfair lock |
Release form | The call to unlock () must be shown | Automatic release monitor |
flexibility | Not flexible enough | |
Lock implementation mechanism | Rely on AQS | Monitor Mode |
- In the part to be further studied, what is the implementation principle of the lock mechanism?