Due to the uncertainty of thread scheduling, if the system thread scheduler pauses somewhere and lets another thread execute (sometimes the sleep() method can be used to force the pause), an error will appear.
Sychronized
In order to solve this problem, Java multithreading introduces the synchronized keyword mechanism.
synchronized(obj){//obj is the synchronization monitor
...
//Synchronous code block
}
Meaning: before a thread starts executing a synchronous code block, it must obtain a lock on the synchronization monitor
Only one thread can obtain the lock on the synchronization monitor at any time. When the synchronization code block is completed, the thread will release the lock on the synchronization monitor.
Generally, the shared resources that may be accessed concurrently are selected as the synchronization monitor. For example, in the case of bank withdrawal, account can be selected as the synchronization monitor.
The synchronized keyword modifies a method, which is a synchronization method. Generally, methods that will change competitive resources are synchronized.
Synchronization Lock: synchronization is achieved by explicitly defining synchronization Lock objects 😊
Locks provide exclusive access to shared resources. Only one thread can Lock the Lock object at a time. Threads should obtain the Lock object before accessing shared resources.
In implementing thread safety, the commonly used lock is reentrantlock (reentrant lock)
private final ReentranLock lock = new ReentranLock();
The Lock object can be used to Lock and release locks on display
public void m(){ //Lock lock.lock() try{ //Need to ensure thread safe code }finally{ lcok.unlock();//Use the finally block to ensure that the lock is released } }
package syn; //Test Lock 🔒 import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) { TestLock2 testLock2 = new TestLock2(); new Thread(testLock2,"a").start(); new Thread(testLock2).start(); new Thread(testLock2).start(); } } class TestLock2 implements Runnable{ int ticketNums = 10; //Define lock 🔒 private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while(true){ try{ lock.lock();//Lock if(ticketNums > 0){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketNums--); }else{ break; } }finally { //Unlock lock.unlock(); } } } }
Comparison between synchronized and Lock
- Lock is a display lock (manually open and close, don't forget to close the lock), and synchronized is an implicit lock, which is automatically released out of the scope.
2.Lock only has code block lock, and synchronized has code block and method 🔒 - Using Lock lock, the JVM will spend less time scheduling threads and perform better. And it has better scalability (providing more subclasses)
- Priority: Lock > sync code block (has entered the method body and allocated corresponding resources) > sync method (outside the method body)
Lock reentry: a thread can lock the locked ReentranLock lock again. The ReentranLock object will maintain a counter to track the nested calls of the lock() method. After calling lock() to lock, the thread must call unlock() to release the lock. Therefore, a piece of lock protected code can call another method protected by the same lock.
deadlock
When two threads wait for each other to release the synchronization monitor, a deadlock will occur. The Java virtual machine does not have a mechanism to deal with deadlock, so try to avoid this situation in multithreaded programming.
When a synchronization block has "locks of more than two objects" at the same time, the problem of "deadlock" may occur.
package syn; public class DeadLock { public static void main(String[] args) { MakeUp g1 = new MakeUp(0,"Cinderella"); MakeUp g2 = new MakeUp(1,"Snow White"); g1.start(); g2.start(); } } class Lipstick{ } class Mirror{ } class MakeUp extends Thread{ static Lipstick lipstick = new Lipstick(); static Mirror mirror = new Mirror(); int choice; String girlName; MakeUp(int choice, String girlName) { this.choice = choice; this.girlName = girlName; } @Override public void run() { try { makeup(); } catch (InterruptedException e) { e.printStackTrace(); } } private void makeup() throws InterruptedException { if(choice == 0){ synchronized (lipstick) { System.out.println(this.girlName + "Get lipstick lock"); Thread.sleep(1000); synchronized (mirror) { System.out.println(this.girlName + "Get the lock of the mirror"); Thread.sleep(2000); } } }else{ synchronized (mirror) { System.out.println(this.girlName + "Get the lock of the mirror"); Thread.sleep(2000); synchronized (lipstick) { System.out.println(this.girlName + "Get lipstick lock"); } } } } }
As shown in the figure, the program only executes the first line of code in two cases. In case of deadlock, the whole program will not have any exception or give any prompt, but all threads are blocked and cannot continue.
Modify the code as follows to avoid deadlock.
if (choice == 0) { synchronized (lipstick) {//Get lipstick lock System.out.println(this.girlName + "Get lipstick lock"); Thread.sleep(1000); } synchronized (mirror) {//Obtain the lock of the mirror after 1s System.out.println(this.girlName + "Get the lock of the mirror"); } } else { synchronized (mirror) {//Get the lock of the mirror System.out.println(this.girlName + "Get the lock of the mirror"); Thread.sleep(2000); } synchronized (lipstick) {//After 2s, I want to get the lock of lipstick System.out.println(this.girlName + "Get lipstick lock"); } }
Four necessary conditions for deadlock generation:
1. Mutually exclusive condition: a resource can only be used by one process at a time
2. Request and hold condition: when a process is blocked by requesting resources, it will not let go of the obtained resources.
3. Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived until they are used up
4. Cyclic waiting conditions: a head to tail cyclic waiting resource relationship is formed between several processes.
As long as we find a way to break any one or more of these conditions, deadlock can be avoided.