1. Definition and impact of deadlock

1.1 What is a deadlock

  • In concurrency, multiple threads (processes) do not give way to each other. They hold the resources needed by each other, but do not release them actively. As a result, no one can move forward, resulting in an endless block, known as a deadlock.

  • Like the two men in the picture above, the red player says: You give me the ball first and I will let go; Blue player said: you let go of me first I will give you the ball
  • The professional point is shown below:

  • How can multiple threads (greater than 2) deadlock occur

    Suppose there are three threads: Thread 1 holds lock A and wants to acquire lock B; Thread 2 holds lock B and wants to acquire lock C; Thread 3 holds lock C and wants to acquire lock A; The three forms a ring, and no one takes the initiative to release the lock first, and no one can obtain the required lock, which forms a deadlock between multiple threads.

1.2 Impact of deadlock

  • Deadlocks vary from system to system, and deadlocks are not necessarily harmful, depending on how well the system handles deadlocks.
  1. Databases: Some databases can detect and abandon deadlocks.
  2. JVM: Cannot be processed automatically, but can be detected.
  • JVM deadlocks are rare but dangerous, and when they do occur, many are in high-concurrency scenarios that can affect users if the system crashes. In addition, concurrent stress tests cannot all identify potential deadlocks.

2. Deadlock example

2.1 Deadlock simple case

  • In the example above, two threads are waiting for each other. Thread1 has acquired the lock of object1, thread2 has acquired the lock of Object2, and then wants to acquire the lock of each other. Since the two threads do not give way to each other, the program will not stop
/ * * *@author yiren
 */
public class DeadLockMust {
    private static Object object1 = new Object();
    private static Object object2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (object1) {
                System.out.println(Thread.currentThread().getName() + " object1");
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (object2) {
                    System.out.println(Thread.currentThread().getName() + " object2"); }}}); Thread thread2 =new Thread(() -> {
            synchronized (object2) {
                System.out.println(Thread.currentThread().getName() + " object2");
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (object1) {
                    System.out.println(Thread.currentThread().getName() + " object1"); }}}); thread1.start(); thread2.start(); }}Copy the code
Thread-1 object2
Thread-0 object1

Copy the code
  • If the IDEA force point stops, the exit code will change to: 130, while the normal exit code is 0
Process finished with exit code 130 (interrupted by signal 2: SIGINT)
Copy the code

2.2 Transfer Cases

  • Two people transfer money to each other, lock the roll-in and roll-out accounts, create interdependence, and then deadlock.
/** * Two people transfer money *@author yiren
 */
public class DeadlockTransferMoney {

    public static void main(String[] args) throws InterruptedException {
        Account a = new Account(1000);
        Account b = new Account(1000);
        Thread thread1 = new Thread(() -> {
            try {
                transferMoney(a, b,100);
            } catch(InterruptedException e) { e.printStackTrace(); }}); Thread thread2 =new Thread(() -> {
            try {
                transferMoney(b, a, 200);
            } catch(InterruptedException e) { e.printStackTrace(); }}); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("a: " + a);
        System.out.println("b: " + b);
    }


    public static void transferMoney(Account from, Account to, Integer amount) throws InterruptedException {
        synchronized (from) {
            TimeUnit.MILLISECONDS.sleep(500);
            synchronized (to) {
                if (from.balance - amount < 0) {
                    System.out.println("Insufficient balance! Transfer failed!");
                    return;
                }
                from.balance -= amount;
                to.balance += amount;
                System.out.println("Transfer successful, transfer:" + amount + "Yuan"); }}}}Copy the code
  • This procedure and the above thread deadlock case code principle is the same.

2.3 Random transfer cases of multiple people

  • Case realization: transfer money to a user randomly, and lock the roll-in/roll-out account first. Start N threads simultaneously
/ * * *@author yiren
 */
public class DeadlockMultiTransferMoney {
    private static final int ACCOUNTS_NUM = 50;
    private static final int MONEY_NUM = 1000;
    private static final int ITERATIONS_NUM = 1000000;
    private static final int THREAD_NUM = 20;
    private static Account[] accounts = new Account[ACCOUNTS_NUM];

    static {
        for (int i = 0; i < accounts.length; i++) {
            accounts[i] = newAccount(MONEY_NUM); }}public static void main(String[] args) {
        for (int i = 0; i < THREAD_NUM; i++) {
            newTransferThread().start(); }}private static class TransferThread extends Thread {
        @Override
        public void run(a) {
            for (int i = 0; i < ITERATIONS_NUM; i++) {
                int fromAccount = new Random().nextInt(ACCOUNTS_NUM);
                int toAccount = new Random().nextInt(ACCOUNTS_NUM);
                int amount = new Random().nextInt(MONEY_NUM);
                try {
                    transferMoney(accounts[fromAccount], accounts[toAccount], amount);
                } catch(InterruptedException e) { e.printStackTrace(); }}}}public static void transferMoney(Account from, Account to, Integer amount) throws InterruptedException {
        synchronized (from) {
            TimeUnit.MILLISECONDS.sleep(100);
            synchronized (to) {
                if (from.balance - amount < 0) {
                    System.out.println("Insufficient balance! Transfer failed!");
                    return;
                }
                from.balance -= amount;
                to.balance += amount;
                System.out.println("Transfer successful, transfer:" + amount + "Yuan"); }}}}Copy the code
  • After the program is started, the output will stop after running for a period of time. Although mutual transfer between two accounts is a small probability event among many accounts, it will still happen, and mutual transfer will form deadlock.
  • Not only does a deadlock occur when you transfer money to each other, but it also occurs when N people form a loop. To review the three threads case, replace locks A, B, and C with three accounts.

Suppose there are three threads: Thread 1 holds lock A and wants to acquire lock B; Thread 2 holds lock B and wants to acquire lock C; Thread 3 holds lock C and wants to acquire lock A; The three forms a ring, and no one takes the initiative to release the lock first, and no one can obtain the required lock, which forms a deadlock between multiple threads.

3. Four necessary conditions for deadlocks

  1. Mutually exclusive condition: a resource can only be used by one thread at a time.

  2. Request and hold conditions: a thread holds the first lock, then requests a second lock, then can’t get the second lock, and waits without releasing the first lock.

  3. Non-deprivation condition: multi-threads compete for resources. When they hold the resources needed by each other, threads will not be interfered by external factors and deprived of their right to compete.

  4. Cyclic waiting condition: when multiple threads compete for resources and hold each other’s resources, they form a loop (cyclic dependence) that cannot be undone.

4. Locate the deadlock

4.1 JStack Deadlock Location Description

  1. Take DeadlockMust, above
  • Let’s first find the PID –> usejpsThe command
> jps
52049 Launcher
52050 DeadlockMust
52106 Jps
46876
...
Copy the code
  • Then use jStack PID
> jstack 52020-02-14 19:15:57 Full Thread Dump Java HotSpot(TM) 64-bit Server VM (25.241-b07 mixed mode):"Attach Listener" #14 daemon prio=9 os_prio=31 tid=0x00007fdfbe840000 nid=0xa403 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #13 prio=5 os_prio=31 tid=0x00007fdfc1099800 nid=0x1003 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE ....... . Found one Java-level deadlock: ============================="Thread-1":
  waiting to lock monitor 0x00007fdfbd011f78 (object 0x000000076ac2a7e8, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007fdfbd014758 (object 0x000000076ac2a7f8, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
	at com.imyiren.concurrency.deadlock.DeadlockMust.lambda$mainThe $1(DeadlockMust.java:35)
	- waiting to lock <0x000000076ac2a7e8> (a java.lang.Object)
	- locked <0x000000076ac2a7f8> (a java.lang.Object)
	at com.imyiren.concurrency.deadlock.DeadlockMust$$Lambda$2/2129789493.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)
"Thread-0":
	at com.imyiren.concurrency.deadlock.DeadlockMust.lambda$main$0(DeadlockMust.java:22)
	- waiting to lock <0x000000076ac2a7f8> (a java.lang.Object)
	- locked <0x000000076ac2a7e8> (a java.lang.Object)
	at com.imyiren.concurrency.deadlock.DeadlockMust$$LambdaThe $1/1607521710.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.
Copy the code
  • Some information in the middle is omitted.
  • We see firstFound one Java-level deallock:Tell us we found a deadlock. Then the following tells us which lock thread-1 and Thread0 wait for respectively, and who holds the lock. As follows:Thread-1Waiting for the0x00007fdfbd011f78Thread - 0
"Thread-1":
  waiting to lock monitor 0x00007fdfbd011f78 (object 0x000000076ac2a7e8, a java.lang.Object),
  which is held by "Thread-0"
Copy the code
  • And then we findJava stack information for the threads listed above:The following information
  • seeThread-1:- waiting to lock <0x000000076ac2a7e8>Waiting for the* * * * 7 e8 lock;locked <0x000000076ac2a7f8>hold****7f8
"Thread-1":
	at com.imyiren.concurrency.deadlock.DeadlockMust.lambda$mainThe $1(DeadlockMust.java:35)
	- waiting to lock <0x000000076ac2a7e8> (a java.lang.Object)
	- locked <0x000000076ac2a7f8> (a java.lang.Object)
	at com.imyiren.concurrency.deadlock.DeadlockMust$$Lambda$2/2129789493.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)
Copy the code
  • seeThread - 10:– waiting to lock <0x000000076ac2a7f8>Waiting for the* * * * 7 f8 to lock;locked <0x000000076ac2a7e8>hold****7e8`
"Thread-0":
	at com.imyiren.concurrency.deadlock.DeadlockMust.lambda$main$0(DeadlockMust.java:22)
	- waiting to lock <0x000000076ac2a7f8> (a java.lang.Object)
	- locked <0x000000076ac2a7e8> (a java.lang.Object)
	at com.imyiren.concurrency.deadlock.DeadlockMust$$LambdaThe $1/1607521710.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)
Copy the code
  1. Let’s go back to the topDeadlockMultiTransferMoneyClass deadlock, directly look atjstackinformation
Found one Java-level deadlock:
=============================
"Thread-19":
  waiting to lock monitor 0x00007fc7f401a738 (object 0x000000076ac2f280, a com.imyiren.concurrency.deadlock.Account),
  which is held by "Thread-1"
"Thread-1":
  waiting to lock monitor 0x00007fc7f700d4b8 (object 0x000000076ac2f1e0, a com.imyiren.concurrency.deadlock.Account),
  which is held by "Thread-15"
"Thread-15":
  waiting to lock monitor 0x00007fc7f700e7f8 (object 0x000000076ac2f3a0, a com.imyiren.concurrency.deadlock.Account),
  which is held by "Thread-8"
"Thread-8":
  waiting to lock monitor 0x00007fc7f700d408 (object 0x000000076ac2f460, a com.imyiren.concurrency.deadlock.Account),
  which is held by "Thread-5"
"Thread-5":
  waiting to lock monitor 0x00007fc7f8016ff8 (object 0x000000076ac2f270, a com.imyiren.concurrency.deadlock.Account),
  which is held by "Thread-18"
"Thread-18":
  waiting to lock monitor 0x00007fc7f700d408 (object 0x000000076ac2f460, a com.imyiren.concurrency.deadlock.Account),
  which is held by "Thread-5"

Java stack information for the threads listed above:
===================================================
"Thread-19":
	at com.imyiren.concurrency.deadlock.DeadlockMultiTransferMoney.transferMoney(DeadlockMultiTransferMoney.java:49)
	- waiting to lock <0x000000076ac2f280> (a com.imyiren.concurrency.deadlock.Account)
	at com.imyiren.concurrency.deadlock.DeadlockMultiTransferMoney$TransferThread.run(DeadlockMultiTransferMoney.java:38)
"Thread-1":
	at com.imyiren.concurrency.deadlock.DeadlockMultiTransferMoney.transferMoney(DeadlockMultiTransferMoney.java:51)
	- waiting to lock <0x000000076ac2f1e0> (a com.imyiren.concurrency.deadlock.Account)
	- locked <0x000000076ac2f280> (a com.imyiren.concurrency.deadlock.Account)
	at com.imyiren.concurrency.deadlock.DeadlockMultiTransferMoney$TransferThread.run(DeadlockMultiTransferMoney.java:38) .... . Found 1 deadlock.Copy the code
  • Note:jstackIt doesn’t necessarily tell us exactly what’s happening with deadlocksjstackIf we can’t analyze it, we can analyze it by ourselves.

4.2 Code to locate deadlock – ThreadMXBean

@author yiren */ public class ThreadMxBeanDetection {private static Object object1 = new Object(); private static Object object2 = new Object(); public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(() -> { synchronized (object1) { System.out.println(Thread.currentThread().getName() +" object1");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (object2) {
                    System.out.println(Thread.currentThread().getName() + " object2"); }}}); Thread thread2 = new Thread(() -> { synchronized (object2) { System.out.println(Thread.currentThread().getName() +" object2");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (object1) {
                    System.out.println(Thread.currentThread().getName() + " object1"); }}}); thread1.start(); thread2.start(); // Wait for deadlocks to occur timeunit.seconds.sleep (2); ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();if(null ! = deadlockedThreads && deadlockedThreads.length > 0) {for (int i = 0; i < deadlockedThreads.length; i++) {
                ThreadInfo threadInfo = threadMXBean.getThreadInfo(deadlockedThreads[i]);
                System.out.println("found deadlock thread: "+ threadInfo.getThreadName()); }}}}Copy the code
Thread-0 object1
Thread-1 object2
found deadlock thread: Thread-1
found deadlock thread: Thread-0
Copy the code

5. Fix deadlock policies

5.1 What Can I Do if a Deadlock Occurs in the Production Environment?

  • Generally, deadlocks occur online, which can not be predicted in advance, and spread very quickly, and the impact is particularly large, so we need to take precautions.

  • If it does happen, the crime scene should be preserved first and service restored quickly. Rectify the fault after recovery.

5.2 Repair Policy 1: Avoiding policies

  • The change of hands scheme of philosopher repast, the change of order scheme of transfer
  1. Philosophers’ Dining Problems: Wiki: Philosophers’ Dining Problems click to see
  • Deadlock occurs: every philosopher takes the left cutlery, then the right cutlery, and is stuck waiting.
/** ** philosopher's question **@author yiren
 */
public class DiningPhilosophers {

    public static void main(String[] args) {
        Philosopher[] philosophers = new Philosopher[5];
        Object[] objects = new Object[philosophers.length];
        for (int i = 0; i < objects.length; i++) {
            objects[i] = new Object();
        }
        for (int i = 0; i < philosophers.length; i++) {
            philosophers[i] = new Philosopher(objects[i], objects[(i+1)%objects.length]);
            new Thread(philosophers[i], "philosopher-" + (i + 1)).start(); }}private static class Philosopher implements Runnable {
        private Object left;
        private Object right;

        public Philosopher(Object left, Object right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public void run(a) {
            try {
                while (true) {
                    // thinking...
                    action("thinking...");
                    synchronized (left) {
                        // Pick up left.
                        action("Pick up left ");
                        synchronized (right) {
                            // Pick up right and eating
                            action("Pick up right and eating ");
                            action("Put down right");
                        }
                        action("Put down left"); }}}catch(InterruptedException e) { e.printStackTrace(); }}private void action(String action) throws InterruptedException {
            System.out.println(Thread.currentThread().getName() + " do " + action);
            TimeUnit.MILLISECONDS.sleep(new Random().nextInt(10)); }}}Copy the code
  • Avoid strategic resolution:

    • Get a waiter to coordinate and hand out the dishes
    • Change the order in which a philosopher holds the cutlery
    • Subtracting one amount of semaphore from a cutlery is equivalent to a meal ticket
  • Detection and Recovery strategy:

    • A third party dares to deprive a philosopher of his food
  • Change the order in which a philosopher holds the cutlery

    We can replace the left and right order of the philosopher with the upper mian function:

    
        public static void main(String[] args) {
            Philosopher[] philosophers = new Philosopher[5];
            Object[] objects = new Object[philosophers.length];
            for (int i = 0; i < objects.length; i++) {
                objects[i] = new Object();
            }
            for (int i = 0; i < philosophers.length; i++) {
                if (philosophers.length - 1 == i) {
                    philosophers[i] = new Philosopher(objects[(i + 1) % objects.length], objects[i]);
                } else {
                    philosophers[i] = new Philosopher(objects[i], objects[(i + 1) % objects.length]);
                }
                new Thread(philosophers[i], "philosopher-" + (i + 1)).start(); }}Copy the code
  1. Mutual transfer problem: we can correct the sequence of obtaining locks in the above two transfers, so that the sequence of obtaining locks in mutual transfers is the same. The code is as follows:
/** * Two people transfer money to each other to fix deadlock **@author yiren
 */
public class DeadlockTransferMoneyFix {

    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Account a = new Account(1000);
        Account b = new Account(1000);
        Thread thread1 = new Thread(() -> {
            try {
                transferMoney(a, b, 100);
            } catch(InterruptedException e) { e.printStackTrace(); }}); Thread thread2 =new Thread(() -> {
            try {
                transferMoney(b, a, 200);
            } catch(InterruptedException e) { e.printStackTrace(); }}); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("a: " + a);
        System.out.println("b: " + b);
    }


    public static void transferMoney(Account from, Account to, Integer amount) throws InterruptedException {
        int fromHashCode = from.hashCode();
        int toHashCode = to.hashCode();
        if (fromHashCode > toHashCode) {
            synchronized (from) {
                synchronized(to) { transfer(from, to, amount); }}return;
        } else if (fromHashCode < toHashCode) {
            synchronized (to) {
                synchronized(from) { transfer(from, to, amount); }}}else {
            synchronized(lock) { transfer(from, to, amount); }}}private static void transfer(Account from, Account to, Integer amount) {
        if (from.balance - amount < 0) {
            System.out.println("Insufficient balance! Transfer failed!");
            return;
        }
        from.balance -= amount;
        to.balance += amount;
        System.out.println("Transfer successful, transfer:" + amount + "Yuan"); }}Copy the code
Transfer: 100 Yuan Transfer: 200 yuan A: Account{balance=1100} B: Account{balance=900} Process finished withexit code 0
Copy the code
  • Use in codehashCodeBecause hashCode is repetitive, so we have to deal with equals, but in business we can do a sort using account unique IDS or something like that

5.3 Recovery Policy 2: Check recovery policies

  • Periodically detect for deadlocks and, if so, strip a resource to break a deadlock

  • Detection algorithm: call link graph of lock

    1. Deadlocks are allowed

    2. Each invocation is logged

    3. Periodically check for loops in the Lock Invocation Link Diagram

    4. If it exists, it means a deadlock has occurred

    5. We then resolve the deadlock with the specified recovery strategy

  • Recovery policy:

    1. Process termination:
      • Terminates the threads one by one until the deadlock is resolved
      • Termination order: Consider (1) priority, (2) resource occupancy ratio, and (3) elapsed time
    2. Resource preemption:
      • The locks that were given out were picked up
      • It’s cheap to let a thread back a few steps without terminating the thread altogether
      • Disadvantages: May cause some threads to not run, resulting in hunger

5.4 Repair Strategy three: Ostrich strategy

  • Ostrich strategy is when the ostrich encounters danger and difficulty, always bury his head in the sand, motionless, is the meaning of stealing a bell from his ears. If we know the probability of a deadlock is extremely low, we can simply ignore it, wait until it happens, and then manually fix it.

6. Avoid deadlocks

6.1 Method 1: Set the timeout period

  • Lock的tryLock(long timeout, TimeUnit unit)

  • Synchronized does not have this ability to attempt to acquire a lock

  • There are many possible causes of timeouts, such as deadlocks, loops, and slow code execution

  • In any case, whenever the timeout is up, we consider it a lock failure, and then do failure processing, log, alarm, restart, etc.

/** * Use tryLock to avoid deadlocks **@author yiren
 */
public class DeadlockTryLock {
    private static Lock lock1 = new ReentrantLock();
    private static Lock lock2 = new ReentrantLock();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    if (lock1.tryLock(1, TimeUnit.SECONDS)) {
                        System.out.println(Thread.currentThread().getName() + " got lock 1");
                        TimeUnit.MILLISECONDS.sleep(new Random().nextInt(10));
                        if (lock2.tryLock(1, TimeUnit.SECONDS)) {
                            System.out.println(Thread.currentThread().getName() + " got lock1 and lock2 successfully.");
                            lock2.unlock();
                            lock1.unlock();
                            break;
                        } else {
                            System.out.println(Thread.currentThread().getName() + " fail to get lock2");
                            lock1.unlock();
                        }
                        TimeUnit.MILLISECONDS.sleep(new Random().nextInt(10));
                    } else {
                        System.out.println(Thread.currentThread().getName() + " fail to get lock1"); }}catch(InterruptedException e) { e.printStackTrace(); }}}); Thread thread2 =new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    if (lock2.tryLock(1, TimeUnit.SECONDS)) {
                        System.out.println(Thread.currentThread().getName() + " got lock 2");
                        TimeUnit.MILLISECONDS.sleep(new Random().nextInt(10));
                        if (lock1.tryLock(1, TimeUnit.SECONDS)) {
                            System.out.println(Thread.currentThread().getName() + " got lock2 and lock1 successfully.");
                            lock1.unlock();
                            lock2.unlock();
                            break;
                        } else {
                            System.out.println(Thread.currentThread().getName() + " fail to get lock1");
                            lock2.unlock();
                        }
                        TimeUnit.MILLISECONDS.sleep(new Random().nextInt(10));
                    } else {
                        System.out.println(Thread.currentThread().getName() + " fail to get lock2"); }}catch(InterruptedException e) { e.printStackTrace(); }}}); thread1.start(); thread2.start(); }}Copy the code
Thread-0 got lock 1
Thread-1 got lock 2
Thread-1 fail to get lock1
Thread-0 got lock1 and lock2 successfully.
Thread-1 got lock 2
Thread-1 got lock2 and lock1 successfully.

Process finished with exit code 0
Copy the code

6.2 Using concurrent classes

  • ` ConcurrentHashMap, ConcurrentLinkedQueue, etc

  • Under the Java. Util. Concurrent. Aotmic. * atomic classes is simple to use and high efficiency

  • Use concurrent collections in preference to synchronous collections

6.3 Other points

  • When we add locks, we try to reduce the granularity of the locks and use different locks to do different things without using one lock

  • Can use synchronized code block without synchronization method, and their own specified lock object, convenient control

  • Name threads as much as possible to make them meaningful for troubleshooting.

  • We should try to avoid lock nesting, which can cause deadlocks

  • Before allocating resources, think about getting them back: the banker algorithm

  • Try not to use the same lock for multiple functions: dedicated lock

7. Other active faults

  • Deadlocks are the most common type of active problem. In addition to deadlocks, there are similar problems that can cause programs to fail to execute successfully. They are collectively called active problems. That is, live lock, hunger

7.1 LiveLock

  • What is a live lock?

    A live lock is a process in which a task or performer is not blocked, but keeps repeating trial-fail-trial-failure because some condition is not met. An entity in a live lock is in a state of constant change, and the live lock may unlock itself.

    • For example: philosopher’s question, pick up the left tableware at the same time, wait for 5 minutes, put it down at the same time, wait for 5 minutes, and try to take the left tableware….. at the same time ; We can set the waiting event to a random number, we can solve

    Although the thread is not blocking, it is always running, but the program does not progress, the thread is always doing the same thing

  • Code demo:

/** * Live lock problem **@author yiren
 */
public class LiveLock {

    private static class Spoon {
        private Diner owner;

        public Spoon(Diner owner) {
            this.owner = owner;
        }

        public synchronized void use(a) {
            System.out.println(owner.name + " has eaten!"); }}private static class Diner {
        private String name;
        private boolean isHungry;

        public Diner(String name) {
            this.name = name;
            this.isHungry = true;
        }

        public void eatWith(Spoon spoon, Diner spouse) {
            while (isHungry) {
                if(spoon.owner ! =this) {
                    try {
                        TimeUnit.MILLISECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }

                if (spouse.isHungry) {
                    System.out.println(name + ":" + spouse.name + " first!");
                    spoon.owner = spouse;
                    continue;
                }

                spoon.use();
                isHungry = false;
                System.out.println(name + " : finished!"); spoon.owner = spouse; }}}public static void main(String[] args) {
        Diner man = new Diner("Man");
        Diner woman = new Diner("Women");

        Spoon spoon = new Spoon(man);

        Thread manThread = new Thread(() -> {
            man.eatWith(spoon,woman);
        });
        Thread womanThread = newThread(() -> { woman.eatWith(spoon,man); }); manThread.start(); womanThread.start(); }}Copy the code
Women : Man first! Man : Women first! Women : Man first! Man : Women first! Women : Man first! Man : Women first! Women : Man first! Man : Women first! Women : Man first! Man : Women first! Women : Man first! Man : Women first! Women : Man first! Man : Women first! Women : Man first! Man : Women first! .Copy the code
  • Giving and taking always circulates in this logic. How do you solve it?

    To add randomness to the retry strategy, modify the eatWith method in the code above

    
            public void eatWith(Spoon spoon, Diner spouse) {
                while (isHungry) {
                    if(spoon.owner ! =this) {
                        try {
                            TimeUnit.MILLISECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        continue;
                    }
    
                    Random random = new Random();
                    if (spouse.isHungry && random.nextInt(10) < 7) {
                        System.out.println(name + ":" + spouse.name + " first!");
                        spoon.owner = spouse;
                        continue;
                    }
    
                    spoon.use();
                    isHungry = false;
                    System.out.println(name + " : finished!"); spoon.owner = spouse; }}}Copy the code
    Man : Women first!
    Women : Man first!
    Man has eaten!
    Man : finished!
    Women has eaten!
    Women : finished!
    
    Process finished with exit code 0
    
    Copy the code

    In addition, we can add retry mechanism. If multiple failures occur, use a compensation policy.

7.2 hunger

  • When a thread needs certain resources but never gets them (such as CPU resources), hunger can lead to poor response performance, long waits or even timeout failures.
  • For example, the thread priority is too low, the thread holds the lock but does not release the lock indefinitely, the program always occupies a resource write lock, and so on
  • Programming should not depend on priorities
    • Thread priorities vary from operating system to operating system, and the priority set in Java may vary from platform to platform
    • In addition, the operating system may change the priority of the thread, making it lower priority

Interview questions are often asked

  1. Write an example of an inevitable deadlock
  2. What conditions must be met for a deadlock to occur
  3. How do I locate deadlocks
  4. What are the strategies for resolving deadlocks?
  5. Talk about the classic philosopher’s dining problem
  6. How to avoid deadlock in practical engineering
  7. What is the activity problem? What’s the difference between a live lock, starvation and a deadlock?

  • Article content source:
  • Java concurrent programming art, JDK1.8 version of the source code, MOOC wukong JUC course

  • Think you can point a thumbs-up 👍 Thanks!

About me

  • Majoring in Computer Science and technology, general university, Hangzhou.
  • Graduated in 20 years, mainly engaged in back-end development of Java technology stack.
  • GitHub: github.com/imyiren
  • Blog : imyi.ren