deadlock
Lock is a very useful tool with many application scenarios. It is very simple to use and easy to understand. but
At the same time, it will also bring some problems, that is, it may cause deadlock. Once a deadlock occurs, the system function will not be able to function
Use.
definition
Thread deadlock means that two or more threads hold the resources needed by each other and will wait for each other to release the resources. If none of the threads actively release the occupied resources, a deadlock will occur.
Some specific conditions for deadlock generation:
1. Mutually exclusive condition: that is, a resource can only be occupied by one thread until it is released by the thread.
2. Request and hold condition: when a thread is blocked by requesting resources, it will hold on to the obtained resources.
3. No deprivation condition: when a resource is occupied by one thread, other threads cannot deprive the resource.
4. Loop waiting condition: when a deadlock occurs, the waiting thread will form a loop (similar to an endless loop), causing permanent blocking.
Four conditions must be met to cause deadlock. To avoid deadlock, only one of the conditions is not met. The first three are the conditions to be met by the lock. Therefore, to avoid deadlock, the fourth condition must be broken
Main causes of deadlock
Insufficient system resources
The sequence of process running and advancing is inappropriate
Improper allocation of resources
package com.dongguo.sync; import jdk.internal.dynalink.beans.StaticClass; import java.util.concurrent.TimeUnit; /** * @author Dongguo * @date 2021/8/24 0024-14:25 * @description: Analog deadlock */ public class DeadLock { static Object obj1 = new Object(); static Object obj2 = new Object(); public static void main(String[] args) { new Thread(() -> { synchronized (obj1) { System.out.println(Thread.currentThread().getName() + "Holding lock 1, attempting to acquire lock 2"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj2) { System.out.println(Thread.currentThread().getName() + "Lock 1 is held and lock 2 is obtained successfully"); } } }, "ThreadA").start(); new Thread(() -> { synchronized (obj2) { System.out.println(Thread.currentThread().getName() + "Holding lock 2, attempting to acquire lock 1"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj1) { System.out.println(Thread.currentThread().getName() + "Lock 2 is held and lock 1 is obtained successfully"); } } }, "ThreadB").start(); } }
Operation results:
This code will cause a deadlock, causing thread ThreadA and thread ThreadAB to wait for each other to release the lock.
This code just demonstrates the deadlock scenario. In reality, you may not write such code. However, in some more
In complex scenarios, you may encounter such problems. For example, after thread a gets the lock, the lock is not released due to some exceptions
(dead cycle). Or ThreadA gets a database lock, throws an exception when releasing the lock, and does not release it. So ThreadAB
When acquiring a lock, it will wait for ThreadA to release the lock, resulting in a deadlock.
Deadlock verification
Once a deadlock occurs, the business is perceptible, because the service can no longer be provided, so it can only be viewed through the dump thread
Which thread is the problem
Using java command line jstack mode
C:\Users\Administrator>jps 4288 RemoteMavenServer36 10968 Jps 17352 Launcher 13212 DeadLock 13436 C:\Users\Administrator>jstack 13212 2021-08-24 14:32:51 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.301-b09 mixed mode): "DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x000002b418b40000 nid=0x3fa0 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "ThreadB" #12 prio=5 os_prio=0 tid=0x000002b42dc62800 nid=0x2ea4 waiting for monitor entry [0x000000c4c43ff000] java.lang.Thread.State: BLOCKED (on object monitor) at com.dongguo.sync.DeadLock.lambda$main$1(DeadLock.java:39) - waiting to lock <0x00000000eb4b5288> (a java.lang.Object) - locked <0x00000000eb4b5298> (a java.lang.Object) at com.dongguo.sync.DeadLock$$Lambda$2/1480010240.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "ThreadA" #11 prio=5 os_prio=0 tid=0x000002b42dc1a800 nid=0x2a00 waiting for monitor entry [0x000000c4c42ff000] java.lang.Thread.State: BLOCKED (on object monitor) at com.dongguo.sync.DeadLock.lambda$main$0(DeadLock.java:26) - waiting to lock <0x00000000eb4b5298> (a java.lang.Object) - locked <0x00000000eb4b5288> (a java.lang.Object) at com.dongguo.sync.DeadLock$$Lambda$1/2074407503.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "Service Thread" #10 daemon prio=9 os_prio=0 tid=0x000002b42d921800 nid=0x43c0 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000002b42ce5f800 nid=0xc0 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000002b42ce5e000 nid=0xc8c waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000002b42ce5c800 nid=0x2830 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000002b42ce59000 nid=0x41e4 runnable [0x000000c4c3cfe000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) - locked <0x00000000eb492430> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:161) at java.io.BufferedReader.readLine(BufferedReader.java:324) - locked <0x00000000eb492430> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:389) at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:48) "Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000002b42cd7e800 nid=0x3e44 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000002b42cdd5000 nid=0xde0 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000002b42cd52800 nid=0x3004 in Object.wait() [0x000000c4c39fe000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000eb308ee0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144) - locked <0x00000000eb308ee0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216) "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000002b42cd4b800 nid=0x884 in Object.wait() [0x000000c4c38ff000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000eb306c00> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x00000000eb306c00> (a java.lang.ref.Reference$Lock) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) "VM Thread" os_prio=2 tid=0x000002b42cd21800 nid=0x324 runnable "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x000002b418b55000 nid=0xd9c runnable "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000002b418b56800 nid=0x4034 runnable "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000002b418b58000 nid=0x431c runnable "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000002b418b59000 nid=0x3b54 runnable "VM Periodic Task Thread" os_prio=2 tid=0x000002b42d925000 nid=0x4108 waiting on condition JNI global references: 317 Found one Java-level deadlock: ============================= "ThreadB": waiting to lock monitor 0x000002b42cd4fa18 (object 0x00000000eb4b5288, a java.lang.Object), which is held by "ThreadA" "ThreadA": waiting to lock monitor 0x000002b42cd522a8 (object 0x00000000eb4b5298, a java.lang.Object), which is held by "ThreadB" Java stack information for the threads listed above: =================================================== "ThreadB": at com.dongguo.sync.DeadLock.lambda$main$1(DeadLock.java:39) - waiting to lock <0x00000000eb4b5288> (a java.lang.Object) - locked <0x00000000eb4b5298> (a java.lang.Object) at com.dongguo.sync.DeadLock$$Lambda$2/1480010240.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "ThreadA": at com.dongguo.sync.DeadLock.lambda$main$0(DeadLock.java:26) - waiting to lock <0x00000000eb4b5298> (a java.lang.Object) - locked <0x00000000eb4b5288> (a java.lang.Object) at com.dongguo.sync.DeadLock$$Lambda$1/2074407503.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) Found 1 deadlock. C:\Users\Administrator>
Focus on the last Java stack information for the threads listed above:
ThreadB waiting to lock <0x00000000eb4b5288> locked <0x00000000eb4b5298>
ThreadA waiting to lock <0x00000000eb4b5298> locked <0x00000000eb4b5288>
Found 1 deadlock.
ThreadB wait lock 5288, hold lock 5298
Thread a waits for lock 5298 and holds lock 5288
Found 1 deadlock.
If a thread enters an endless loop, causing other threads to wait all the time, in this case, linux can locate it first through top
For Java processes with high CPU consumption, use the top -Hp process id to locate which thread it is, and then use jstack to check
Of course, you can also use visualization tools, such as JVM
The content displayed is the same
Jconsole
How to avoid deadlock:
1. Locking sequence: pay attention to the locking sequence to ensure that each thread locks in the same sequence
When multiple threads need the same locks, but lock in different order, deadlock is easy to occur. If you can ensure that all threads obtain locks in the same order, deadlock will not occur. Of course, this method requires you to know all the locks that may be used in advance, but sometimes it is unpredictable.
2. Lock time limit: set a timeout for
Add a timeout. If a thread fails to successfully obtain all required locks within a given time limit, it will fallback and release all obtained locks, and then wait for a random period of time to retry. However, if there are many threads competing for the same batch of resources at the same time, even if there is a timeout and fallback mechanism, these threads may try repeatedly but never get the lock.
3. Deadlock detection: prevention mechanism to ensure that deadlock is found and solved at the first time
Deadlock detection means that whenever a thread obtains a lock, it will be recorded in the thread and lock related data structures (map, graph, etc.). In addition, whenever a thread requests a lock, it also needs to be recorded in this data structure. Deadlock detection is a better deadlock prevention mechanism. It is mainly aimed at those scenarios where sequential locking is impossible and lock timeout is not feasible.
Several common ways to avoid deadlock.
·Avoid a thread acquiring multiple locks at the same time.
·Avoid a thread occupying multiple resources in the lock at the same time, and try to ensure that each lock occupies only one resource.
·Try to use timing lock and use lock.tryLock (timeout) instead of using internal lock mechanism.
·For database locks, locking and unlocking must be in a database connection, otherwise unlocking will fail.
Livelock
A livelock occurs when two threads change each other's end conditions, and finally no one can end,
for example
package com.dongguo.lock; import java.util.concurrent.TimeUnit; /** * @author Dongguo * @date 2021/9/12 0012-14:15 * @description: */ public class LiveLockDemo { static volatile int count = 10; static final Object lock = new Object(); public static void main(String[] args) { new Thread(() -> { // Expect to decrease to 0 and exit the loop while (count > 0) { try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName()+" count="+count); } }, "t1").start(); new Thread(() -> { // Expect more than 20 to exit the cycle while (count < 20) { try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } count++; System.out.println(Thread.currentThread().getName()+" count="+count); } }, "t2").start(); } }
The initial count is 10
t1 thread executes count - operation. It is expected to continue to execute when the count value is 0
The t2 thread executes the count + + operation, and continues to execute when the count value is expected to be 20
t1 keeps executing count - operations and t2 keeps executing count + + operations, resulting in two threads unable to meet the conditions.
hunger
When multiple threads access the same synchronous resource, some threads always have no chance to get the mutex. This is called starvation.
In many tutorials, hunger is defined as that a thread cannot be scheduled and executed by the CPU due to its low priority
There are three situations of hunger
a. High priority threads devour the CPU time slice of low priority threads
Theoretically, threads with high thread priority will get more execution opportunities than threads with low thread priority,
After testing, the occurrence frequency of high priority will be much higher than that of low priority
Different operating systems have different priority support for threads. The specification is between 1-10. java shields the underlying differentiation of this operating system through three constants.
b. The thread is permanently blocked waiting to enter a synchronized code block
c. The waiting thread is never awakened
give an example
For example, if synchronized is a non fair lock and ReentrantLock is a non fair lock by default, one or more threads may preempt all resources, resulting in some threads unable to get the chance to run.
ReentrantLock unfair lock: a case of three ticket sellers selling 100 tickets
package com.dongguo.concurrent.synchronize; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author Dongguo * @date 2021/9/3 0003-10:14 * @description: Realize the case that three conductors sell 100 tickets * Use Lock */ public class SaleTicket { public static void main(String[] args) { Ticket ticket = new Ticket(); new Thread(() -> { //Cycle 100 times to ensure that all tickets can be sold out for (int i = 0; i < 100; i++) { ticket.saleTicket(); } }, "T1").start(); new Thread(() -> { for (int i = 0; i < 100; i++) { ticket.saleTicket(); } }, "T2").start(); new Thread(() -> { for (int i = 0; i < 100; i++) { ticket.saleTicket(); } }, "T3").start(); } } /** * @author Dongguo * @description: Resource class */ class Ticket { private int count = 100; //Create reentrant lock ReentrantLock private Lock lock = new ReentrantLock();//The default is false public void saleTicket() { //Lock lock.lock(); try { if (count > 0) { count--; System.out.println(Thread.currentThread().getName() + "Tickets sold successfully, there are still" + count + "Tickets!"); } } catch (Exception e) { e.printStackTrace(); } finally { //Unlock lock.unlock(); } } }
Operation results:
T1 Tickets sold successfully. There are 99 tickets left! T1 Tickets sold successfully. There are 98 tickets left! T1 Tickets sold successfully, 97 tickets left! T1 Tickets sold successfully, 96 tickets left! T1 Tickets sold successfully. There are 95 tickets left! T1 Tickets sold successfully. There are 94 tickets left! T1 Tickets sold successfully. There are 93 tickets left! T1 Tickets sold successfully. There are 92 tickets left! T1 Tickets sold successfully, 91 tickets left! T1 Tickets sold successfully. There are 90 tickets left! T1 Tickets sold successfully. There are 89 tickets left! T1 Tickets sold successfully. There are 88 tickets left! T1 Tickets sold successfully. There are 87 tickets left! T1 Tickets sold successfully. There are 86 tickets left! T1 Tickets sold successfully. There are 85 tickets left! T1 Tickets sold successfully. There are 84 tickets left! T1 Tickets sold successfully. There are 83 tickets left! T1 Tickets sold successfully. There are 82 tickets left! T1 Tickets sold successfully. There are 81 tickets left! T1 Tickets sold successfully. There are 80 tickets left! T1 Tickets sold successfully. There are 79 tickets left! T1 Tickets sold successfully. There are 78 tickets left! T1 Tickets sold successfully. There are 77 tickets left! T1 Tickets sold successfully. There are 76 tickets left! T1 Tickets sold successfully. There are 75 tickets left! T1 Tickets sold successfully. There are 74 tickets left! T1 Tickets sold successfully. There are 73 tickets left! T1 Tickets sold successfully. There are 72 tickets left! T1 Tickets sold successfully. There are 71 tickets left! T1 Tickets sold successfully. There are 70 tickets left! T1 Tickets sold successfully. There are 69 tickets left! T1 Tickets sold successfully. There are 68 tickets left! T1 Tickets sold successfully. There are 67 tickets left! T1 Tickets sold successfully. There are 66 tickets left! T1 Tickets sold successfully. There are 65 tickets left! T1 Tickets sold successfully. There are 64 tickets left! T1 Tickets sold successfully. There are 63 tickets left! T1 Tickets sold successfully. There are 62 tickets left! T1 Tickets sold successfully. There are 61 tickets left! T1 Tickets sold successfully. There are 60 tickets left! T1 Tickets sold successfully. There are 59 tickets left! T1 Tickets sold successfully. There are 58 tickets left! T1 Tickets sold successfully. There are 57 tickets left! T1 Tickets sold successfully. There are 56 tickets left! T1 Tickets sold successfully. There are 55 tickets left! T1 Tickets sold successfully. There are 54 tickets left! T1 Tickets sold successfully. There are 53 tickets left! T1 Tickets sold successfully. There are 52 tickets left! T1 Tickets sold successfully. There are 51 tickets left! T1 Tickets sold successfully. There are 50 tickets left! T1 Tickets sold successfully. There are 49 tickets left! T1 Tickets sold successfully. There are 48 tickets left! T1 Tickets sold successfully. There are 47 tickets left! T1 Tickets sold successfully. There are 46 tickets left! T1 Tickets sold successfully. There are 45 tickets left! T1 Tickets sold successfully. There are 44 tickets left! T1 Tickets sold successfully. There are 43 tickets left! T1 Tickets sold successfully. There are 42 tickets left! T1 Tickets sold successfully. There are 41 tickets left! T1 Tickets sold successfully. There are 40 tickets left! T1 Tickets sold successfully. There are 39 tickets left! T1 Tickets sold successfully. There are 38 tickets left! T1 Tickets sold successfully. There are 37 tickets left! T1 Tickets sold successfully. There are 36 tickets left! T1 Tickets sold successfully. There are 35 tickets left! T1 Tickets sold successfully. There are 34 tickets left! T1 Tickets sold successfully. There are 33 tickets left! T1 Tickets sold successfully. There are 32 tickets left! T1 Tickets sold successfully. There are 31 tickets left! T1 Tickets sold successfully. There are 30 tickets left! T1 Tickets sold successfully. There are 29 tickets left! T1 Tickets sold successfully. There are 28 tickets left! T2 Tickets sold successfully. There are 27 tickets left! T2 Tickets sold successfully. There are 26 tickets left! T2 Tickets sold successfully. There are 25 tickets left! T2 Tickets sold successfully. There are 24 tickets left! T2 Tickets sold successfully. There are 23 tickets left! T2 Tickets sold successfully. There are 22 tickets left! T2 Tickets sold successfully. There are 21 tickets left! T2 Tickets sold successfully. There are 20 tickets left! T2 Tickets sold successfully. There are 19 tickets left! T2 Tickets sold successfully. There are 18 tickets left! T2 Tickets sold successfully. There are 17 tickets left! T2 Tickets sold successfully. There are 16 tickets left! T2 Tickets sold successfully. There are 15 tickets left! T2 Tickets sold successfully. There are 14 tickets left! T2 Tickets sold successfully. There are 13 tickets left! T2 Tickets sold successfully. There are 12 tickets left! T2 Tickets sold successfully. There are 11 tickets left! T2 Tickets sold successfully. There are 10 tickets left! T2 Tickets sold successfully. There are 9 tickets left! T2 Tickets sold successfully. There are 8 tickets left! T2 Tickets sold successfully. There are 7 tickets left! T2 Tickets sold successfully. There are 6 tickets left! T2 Tickets sold successfully. There are 5 tickets left! T2 Tickets sold successfully. There are 4 tickets left! T2 Tickets sold successfully. There are 3 tickets left! T2 Tickets sold successfully. There are 2 tickets left! T2 Tickets sold successfully, 1 ticket left! T2 Tickets sold successfully. There are 0 tickets left!
All tickets were sold by threads t1 and t2, and thread t3 did not run.
For example, in ReentrantReadWriteLock
Once there are many read operations, it becomes more difficult to obtain the write lock,
If there are currently 1000 threads, 999 reads and 1 write, it is possible that 999 read threads have grabbed the lock for a long time, and that one write thread is tragic
Because there may always be a read lock, but the write lock cannot be obtained, and there is no chance to write at all
How to avoid hunger
a. Set reasonable priorities
b. Use fair locks instead of synchronized mutexes (you can use postmark lock StampedLock instead of ReentrantReadWriteLock)