Java Concurrency Foundation Learning - Talk about Deadlocks

Preface

The first few blogs are a simple introduction to java multithreading basics, but they do not summarize deadlocks. This blog also summarizes deadlocks in detail.

What is deadlock

Deadlock occurs in concurrency when two (or more) threads (or processes) hold the resources they need from each other and do not actively release each other, causing all requests to fail to move forward, resulting in endless blocking of the program.

A code instance where deadlocks are inevitable

/**
 * autor:liman
 * createtime:2021/9/23
 * comment:Deadlock example
 */
public class SimpleDeadLockDemo implements Runnable{

    int flag = 1;
    static Object object01 = new Object();
    static Object object02 = new Object();

    public static void main(String[] args) {
        SimpleDeadLockDemo deadLockDemo01 = new SimpleDeadLockDemo();
        SimpleDeadLockDemo deadLockDemo02 = new SimpleDeadLockDemo();
        deadLockDemo01.flag=1;
        deadLockDemo02.flag=0;
        Thread thread01 = new Thread(deadLockDemo01);
        Thread thread02 = new Thread(deadLockDemo02);
        thread01.start();
        thread02.start();

    }

    @Override
    public void run() {
        System.out.println("Current flag="+flag);
        if(1==flag){
            synchronized (object01){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (object02){
                    System.out.println("escape dead lock,flag:1");
                }
            }
        }else{
            synchronized (object02){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (object01){
                    System.out.println("escape dead lock,flag:1");
                }
            }
        }
    }
}

These two codes are actually two threads that hold the lock each other needs and do not release it actively, which prevents them from running any further.

The actual interaction diagram of the above code is shown below

Deadlocks can also occur if there is a looping dependency between multiple threads and a looping lock dependency. As shown below

Thread 1 owns lock A and wants to acquire lock B; Thread 2 owns Lock B and wants to acquire Lock C; Thread 3 has lock C and wants to acquire lock A. In this way, they form a resource-dependent loop, and deadlocks may occur.

The impact of deadlocks on different systems is also different, because it depends on the ability of different systems to handle deadlocks. For example, data itself has the ability to detect and handle deadlocks. However, in JVM, there is no ability to automatically handle deadlocks, which is a security consideration for JVM.

Deadlocks do not necessarily occur, but they do occur over time if they are not completely avoided at the code level. Once they occur, they have a wide range of impacts and are most likely to cause a system crash. In our application development process, stress testing does not necessarily replicate deadlocks.

Deadlock conditions

Having become familiar with what a deadlock is and the dangers of it, we will discuss the necessary conditions for a deadlock to occur on the basis of the relevant examples. First, we will look at several examples.

1. Examples of two-person transfers that are not logically different from deadlocks

In order to be safe, in fact, when transferring money, it is also necessary to obtain the locks of accounts and accounts.

/**
 * autor:liman
 * createtime:2021-10-30
 * comment:Examples of transfers causing deadlocks
 */
public class TransferAccount implements Runnable {

    int flag = 1;
    static Account fromAccount = new Account(500);
    static Account toAccount = new Account(500);

    public static void main(String[] args) throws InterruptedException {
        TransferAccount transferAccountOne = new TransferAccount();
        TransferAccount transferAccountTwo = new TransferAccount();
        transferAccountOne.flag = 0;
        transferAccountTwo.flag = 1;
        //Thread 1, from to account to front account
        Thread threadOne = new Thread(transferAccountOne);
        //Thread 2, from from account to to account
        Thread threadTwo = new Thread(transferAccountTwo);
        threadOne.start();
        threadTwo.start();
        threadOne.join();
        threadTwo.join();
        System.out.println("account from Balance of:"+fromAccount.balance);
        System.out.println("account to Balance of:"+toAccount.balance);
    }

    @Override
    public void run() {
        if (flag == 1) {
            //from From Account to To Account
            transMoney(fromAccount, toAccount, 200);
        }

        if (flag == 0) {
            //Transfer from to account to front account
            transMoney(toAccount, fromAccount, 200);
        }
    }

    public static void transMoney(Account fromAccount, Account toAccount, int amount) {
        synchronized (fromAccount) {

            //Analog communication is time consuming and deadlocks are created with this
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (toAccount) {
                if (fromAccount.balance - amount < 0) {
                    System.out.println("Transfer failed due to insufficient balance");
                }
                //Transfer operation
                fromAccount.balance -= amount;
                toAccount.balance = toAccount.balance + amount;
                System.out.println("Transfer succeeded" + amount + "element");
            }
        }
    }

    static class Account {

        public Account(int balance) {
            this.balance = balance;
        }

        int balance;
    }
}

The above code does not necessarily result in a deadlock if there is no code that takes time for analog communication, but with a sleep operation that takes time for analog communication, a deadlock is inevitable because one of the threads acquires another lock without releasing the lock held.

2. Deadlocks also occur when transfers are made randomly between multiple people

/**
 * autor:liman
 * createtime:2021-10-30
 * comment: Multiple simultaneous transfers are still dangerous
 */
public class ManyPersonTransMoney {

    //Number of accounts, randomly pull out more than 500 accounts for transfer
    private static final int NUM_ACCOUNTS = 500;
    private static final int ACCOUNT_MONEY = 1000;
    //Simulate the number of online transfers per second
    private static final int TRANS_MONEY_COUNT = 1000000;
    private static final int NUM_TRANS_THREAD = 20;

    public static void main(String[] args) {
        Random random = new Random();
        Account[] accounts = new Account[NUM_ACCOUNTS];

        for (int i = 0; i < accounts.length; i++) {
            accounts[i] = new Account(ACCOUNT_MONEY);
        }

        class TransferThread extends Thread{
            @Override
            public void run() {
                //Simulate the number of transfers per online (fixed)
                for(int i=0;i<TRANS_MONEY_COUNT;i++){
                    //Randomly get transfer accounts and amounts (the same person can transfer to different people)
                    int fromAcct = random.nextInt(NUM_ACCOUNTS);
                    int toAcct = random.nextInt(NUM_ACCOUNTS);
                    if(fromAcct!=toAcct){
                        int amount = random.nextInt(ACCOUNT_MONEY);
                        //Transfer (reused previous transfer codes,)
			   transMoney(accounts[fromAcct],accounts[toAcct],amount);
                    }
                }
            }
        }

        //Start multiple threads for transfer
        for(int i=0;i<NUM_TRANS_THREAD;i++){
            new TransferThread().start();
        }
    }
    
    public static void transMoney(Account fromAccount, Account toAccount, int amount) {
        synchronized (fromAccount) {
            synchronized (toAccount) {
                if (fromAccount.balance - amount < 0) {
                    System.out.println("Transfer failed due to insufficient balance");
                }
                //Transfer operation
                fromAccount.balance -= amount;
                toAccount.balance = toAccount.balance + amount;
                System.out.println("Transfer succeeded" + amount + "element");
            }
        }
    }
}

There is no logic to add sleep to the transfer, it is just a normal transfer operation, but after a period of time, the program is still stuck

In fact, you can see that deadlocks don't necessarily occur if you don't avoid them at the code level, but they do occur when the program runs for a long time, which seems to satisfy Murphy's Law.

3. Philosopher's Dining Problem

This is a classic deadlock problem that computer majors should have heard about in Operating Systems.

No more details on what philosophers eat, just a simple code simulation

/**
 * autor:liman
 * createtime:2021-10-31
 * comment:Philosopher Dining Problem Simulation
 */
public class DiningPhilosophers {

    public static class Philosopher implements Runnable{

        private Object leftChopstick;
        private Object rightChopstick;

        public Philosopher(Object leftChopstick, Object rightChopstick) {
            this.leftChopstick = leftChopstick;
            this.rightChopstick = rightChopstick;
        }

        @Override
        public void run() {
            try {
                //Philosophers do something endless except think and eat
                while (true) {
                    doAction("Philosophical thinking");
                    synchronized (leftChopstick){
                        doAction("Pick up the chopsticks on the left");
                        synchronized (rightChopstick){
                            doAction("Pick up the chopsticks on the right");
                            doAction("Having dinner......");
                            doAction("Put down the chopsticks on the right");
                        }
                        doAction("Put down the chopsticks on the left");
                    }


                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }

        private void doAction(String action) throws InterruptedException {
            System.out.println(Thread.currentThread().getName()+":"+action);
            Thread.sleep((long) (Math.random()*10));
        }
    }

    public static void main(String[] args) {
        Philosopher[] philosophers = new Philosopher[5];//Initialize Five Philosophers
        Object[] chopsticks = new Object[philosophers.length];//Initialize 5 chopsticks
        for(int i =0;i<chopsticks.length;i++){
            chopsticks[i] = new Object();
        }
        //Instantiate philosophers and start threads
        for(int i =0;i<philosophers.length;i++){
            Object leftChopstick = chopsticks[i];
            Object rightChopstick = chopsticks[(i+1)%chopsticks.length];
            philosophers[i] = new Philosopher(leftChopstick,rightChopstick);
            new Thread(philosophers[i],"philosopher"+(i+1)).start();
        }
    }
}

After still running for a while, a deadlock appears and philosophers start hungry.

Requirements for deadlock

1. Mutual exclusion. The resources that need to be acquired between threads are mutually exclusive.

2. Request and hold. When a thread acquires a resource lock, it does not release the lock, but keeps the lock it holds.

3. Not deprived. No third party forces a thread to release resources

4. Wait in a loop. A thread waits for another resource lock without releasing one. Ring dependencies occur when multiple threads wait for a resource lock.

These four conditions are necessary for a deadlock to occur, that is, a deadlock will only occur if all four conditions are met.

Back to the previous simple instance, all four conditions were met, resulting in a deadlock.

How to locate fixes and avoid

Now that we have introduced what deadlocks are and the four prerequisites for them to occur, it is time to talk about how to avoid and repair them.

How to locate

Deadlock resolution is half successful if it is positioned successfully.

1,jstack

The command provided by JDK detects and locates deadlocks by specifying a pid that can be viewed through jps.

After running for a while with the example of multi-person transfer above, you can specify the pid through jstack and query for the following information

2,ThreadMXBean

This method uses code to detect deadlocks, and if a deadlock occurs, the field can be recorded

/**
 * autor:liman
 * createtime:2021-10-30
 * comment:Detecting deadlocks with ThreadMXBean
 */
public class ThreadMXBeanDetection implements Runnable {

    int flag = 1;
    static Object object01 = new Object();
    static Object object02 = new Object();

    public static void main(String[] args) {
        SimpleDeadLockDemo deadLockDemo01 = new SimpleDeadLockDemo();
        SimpleDeadLockDemo deadLockDemo02 = new SimpleDeadLockDemo();
        deadLockDemo01.flag = 1;
        deadLockDemo02.flag = 0;
        Thread thread01 = new Thread(deadLockDemo01);
        Thread thread02 = new Thread(deadLockDemo02);
        thread01.start();
        thread02.start();
        //Main thread waits 1 second
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Start detecting deadlocks
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        //Returns an array of thread ID s where deadlocks occurred
        long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
        if (null != deadlockedThreads && deadlockedThreads.length > 0) {
            for (int i = 0; i < deadlockedThreads.length; i++) {
                //TODO: Here's a log of our deadlock
                ThreadInfo threadInfo = threadMXBean.getThreadInfo(deadlockedThreads[i]);
                System.out.println(threadInfo.getThreadName()+"Deadlock occurred");
                String lockName = threadInfo.getLockName();
                System.out.println("The relevant lock information is"+lockName);
            }
        }

    }

    @Override
    public void run() {
        System.out.println("Current flag=" + flag);
        if (1 == flag) {
            synchronized (object01) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (object02) {
                    System.out.println("escape dead lock,flag:1");
                }
            }
        } else {
            synchronized (object02) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (object01) {
                    System.out.println("escape dead lock,flag:1");
                }
            }
        }
    }
}

How to Repair

Deadlocks on the program line can be cumbersome and have a wide impact, so we try to avoid them during development. If a deadlock does occur online, you must also protect the case discovery site and restart the server immediately. First of all, ensure the normal and security of online services, then use the information just saved, troubleshoot deadlocks, and subsequent releases of emergency repair.

How to avoid

If you think about it carefully, in fact, from a business perspective, it doesn't really matter who gets the locks first. We can completely avoid deadlocks by adjusting the order in which locks are acquired. We can also introduce deadlock detection mechanisms in our code, forcing a thread to be deprived of resources if a deadlock is found. This is really the core of how we can avoid deadlocks.

For the above multi-person transfer problem, we can actually avoid deadlocks by adjusting the order in which locks are acquired.

/**
 * autor:liman
 * createtime:2021-10-30
 * comment: Multiple simultaneous transfer deadlock repair
 */
public class ManyPersonTransMoneyFix {

    //Number of accounts, randomly pull out more than 500 accounts for transfer
    private static final int NUM_ACCOUNTS = 500;
    private static final int ACCOUNT_MONEY = 1000;
    //Simulate the number of online transfers per second
    private static final int TRANS_MONEY_COUNT = 1000000;
    private static final int NUM_TRANS_THREAD = 20;
    static Object extendLock = new Object();

    public static void main(String[] args) {
        Random random = new Random();
        FixAccount[] accounts = new FixAccount[NUM_ACCOUNTS];

        for (int i = 0; i < accounts.length; i++) {
            accounts[i] = new FixAccount(ACCOUNT_MONEY);
        }

        class TransferThread extends Thread{
            @Override
            public void run() {
                //Simulate the number of accounts per transfer
                for(int i=0;i<TRANS_MONEY_COUNT;i++){
                    //Randomly get transfer accounts and amounts
                    int fromAcct = random.nextInt(NUM_ACCOUNTS);
                    int toAcct = random.nextInt(NUM_ACCOUNTS);
                    if(fromAcct!=toAcct){
                        int amount = random.nextInt(ACCOUNT_MONEY);
                        //Transfer accounts
                        transMoney(accounts[fromAcct],accounts[toAcct],amount);
                    }
                }
            }
        }

        //Start multiple threads for transfer
        for(int i=0;i<NUM_TRANS_THREAD;i++){
            new TransferThread().start();
        }
    }

    /**
     * Functions to simulate transfers
     * @param fromAccount Transfer account
     * @param toAccount     Transfer to account
     * @param amount    Amount of money
     */
    public static void transMoney(FixAccount fromAccount, FixAccount toAccount, int amount) {
        class Helper{
            public void transfer(){
                if (fromAccount.balance - amount < 0) {
                    System.out.println("Transfer failed due to insufficient balance");
                }
                //Transfer operation
                fromAccount.balance -= amount;
                toAccount.balance = toAccount.balance + amount;
                System.out.println("Transfer succeeded" + amount + "element");
            }
        }

        int fromAccountHashCode = System.identityHashCode(fromAccount);
        int toAccountHashCode = System.identityHashCode(toAccount);

        //If the hashCode value of fromAccount is less than the hashCode value of toAccount
        if(fromAccountHashCode<toAccountHashCode) {
            synchronized (fromAccount) {
                synchronized (toAccount) {
                    new Helper().transfer();
                }
            }
        }

        //If the hashCode value of fromAccount is greater than the hashCode value of toAccount
        if(fromAccountHashCode > toAccountHashCode){
            synchronized (toAccount) {
                synchronized (fromAccount) {
                    new Helper().transfer();
                }
            }
        }

        //If hashCode happens, enter extra time
        if(fromAccountHashCode == toAccountHashCode){
            synchronized (extendLock){//Anyone in any order can grab the lock and execute it first
                synchronized (toAccount) {
                    synchronized (fromAccount) {
                        new Helper().transfer();
                    }
                }
            }
        }
    }

    //Simple Account Object
    static class FixAccount {

        public FixAccount(int balance) {
            this.balance = balance;
        }

        int balance;
    }
}

After that, the code above will run happily without deadlocks

For philosophers'dining problems, the deadlock can also be avoided by adjusting the order in which the particular philosopher obtains the cutlery.

/**
 * autor:liman
 * createtime:2021-10-31
 * comment:Philosopher Dining Problem Simulation
 */
public class DiningPhilosophers {

    public static class Philosopher implements Runnable {

        private Object leftChopstick;
        private Object rightChopstick;

        public Philosopher(Object leftChopstick, Object rightChopstick) {
            this.leftChopstick = leftChopstick;
            this.rightChopstick = rightChopstick;
        }

        @Override
        public void run() {
            try {
                //Philosophers do something endless except think and eat
                while (true) {
                    doAction("Philosophical thinking");
                    synchronized (leftChopstick) {
                        doAction("Pick up the chopsticks on the left");
                        synchronized (rightChopstick) {
                            doAction("Pick up the chopsticks on the right");
                            doAction("Having dinner......");
                            doAction("Put down the chopsticks on the right");
                        }
                        doAction("Put down the chopsticks on the left");
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        private void doAction(String action) throws InterruptedException {
            System.out.println(Thread.currentThread().getName() + ":" + action);
            Thread.sleep((long) (Math.random() * 10));
        }
    }

    public static void main(String[] args) {
        Philosopher[] philosophers = new Philosopher[5];//Initialize Five Philosophers
        Object[] chopsticks = new Object[philosophers.length];//Initialize 5 chopsticks
        for (int i = 0; i < chopsticks.length; i++) {
            chopsticks[i] = new Object();
        }
        //Instantiate philosophers and start threads
        for (int i = 0; i < philosophers.length; i++) {
            Object leftChopstick = chopsticks[i];
            Object rightChopstick = chopsticks[(i + 1) % chopsticks.length];

            //One of the philosophers was able to avoid deadlocks unlike other philosophers in the order in which chopsticks were acquired
            if (i == philosophers.length - 1) {//If it's the last philosopher, first get the cutlery on the right, then the cutlery on the left
                philosophers[i] = new Philosopher(rightChopstick, leftChopstick);
            } else {
                philosophers[i] = new Philosopher(leftChopstick, rightChopstick);
            }
            new Thread(philosophers[i], "philosopher" + (i + 1)).start();
        }
    }
}

So far, the world is peaceful and the program is running happily

In addition, there are other operations to avoid deadlocks by introducing a daemon thread that loops repeatedly to see if there is a deadlock or, if there is one, to force the release of related resources. It can also introduce a ticket mechanism, such as a token bucket, to get a ticket when a philosopher wants to eat. Wait, there are not all examples.

In practice, there are several operations to avoid deadlocks

1. Set the time-out for acquiring locks, try tryLock instead of synchronized

2. Use mature concurrency framework or concurrency classes instead of designing locks yourself.

3. Minimize the granularity of locks. Different logic uses different locks instead of a very complex one.

4. Use synchronous code blocks whenever possible

5. Avoid lock nesting

6. Keep it exclusively locked whenever possible

7. Threads have a well-recognized name to facilitate troubleshooting.

A simple example of tryLock is provided here

/**
 * autor:liman
 * createtime:2021-10-31
 * comment: Use tryLock more, set timeout, avoid deadlock
 */
public class TryLockFix implements Runnable {

    static Lock lock1 = new ReentrantLock();
    static Lock lock2 = new ReentrantLock();

    int flag = 1;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if(flag == 1){
                try {
                    if(lock1.tryLock(800,TimeUnit.MILLISECONDS)){
                        System.out.println("thread"+Thread.currentThread().getName()+"Acquired lock1 lock");
                        if(lock2.tryLock(800,TimeUnit.MILLISECONDS)){
                            System.out.println("thread"+Thread.currentThread().getName()+"Acquired lock2 Lock to start normal business");
                            Thread.sleep(new Random().nextInt(1000));
                            //Release two locks after handling the business
                            lock2.unlock();
                            lock1.unlock();
                        }else{
                            System.out.println("thread"+Thread.currentThread().getName()+"Acquired lock1 lock,But not obtained lock2 Lock, to avoid deadlock, now release the acquired lock1 lock");
                            lock1.unlock();//To avoid deadlocks, release acquired locks
                            Thread.sleep(new Random().nextInt(1000));


                        }
                    }else{
                        System.out.println("thread"+Thread.currentThread().getName()+"Not acquired lock1 lock");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            if(flag == 0){
                try {
                    if(lock2.tryLock(800,TimeUnit.MILLISECONDS)){
                        System.out.println("thread"+Thread.currentThread().getName()+"Acquired lock2 lock");
                        if(lock1.tryLock(800,TimeUnit.MILLISECONDS)){
                            System.out.println("thread"+Thread.currentThread().getName()+"Acquired lock1 Lock to start normal business");
                            Thread.sleep(new Random().nextInt(1000));
                            //Release two locks after handling the business
                            lock1.unlock();
                            lock2.unlock();
                        }else{
                            System.out.println("thread"+Thread.currentThread().getName()+"Acquired lock2 lock,But not obtained lock1 Lock, to avoid deadlock, now release the acquired lock1 lock");
                            lock2.unlock();//To avoid deadlocks, release acquired locks
                            Thread.sleep(new Random().nextInt(1000));


                        }
                    }else{
                        System.out.println("thread"+Thread.currentThread().getName()+"Not acquired lock1 lock");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        TryLockFix threadOne = new TryLockFix();
        TryLockFix threadTwo = new TryLockFix();
        threadOne.flag = 1;
        threadTwo.flag = 0;
        new Thread(threadOne).start();
        new Thread(threadTwo).start();
    }
}

Living Lock and Hunger

In addition to deadlocks, there are other active problems, commonly referred to as locks and hunger.

Active Lock

What is a live lock? In fact, to put it simply, threads are waiting for each other, waiting for other threads to finish running, and no one ends normally.

Returning to the philosopher question is similar to the wait time set by five philosophers, when all five philosophers pick up only the fork on the left, everyone finds it deadlocks. They all put down their forks at the same time, and then they all waited for 5 minutes at the same time. After 5 minutes they picked up the left fork at the same time, found that it would cause deadlock, and then put down the fork again. So many times, no one could eat, but every philosopher picked up the fork frequently and put down the fork. This is the live lock. One of the solutions to the looplock problem is to make the retry time step inconsistent.

Here's a simple example of a live lock

/**
 * autor:liman
 * createtime:2021-10-31
 * comment:Active Lock Instance
 */
public class LiveLock {

    //Spoon
    static class Spoon {

        private Diner owner;

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

        public Diner getOwner() {
            return owner;
        }

        public void setOwner(Diner owner) {
            this.owner = owner;
        }

        public synchronized void use() {
            System.out.printf("%s I'm full!", owner.name);


        }
    }

    static class Diner {

        private String name;
        private boolean isHungry;

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

        public void eatWith(Spoon spoon, Diner spouse) {
            while (isHungry) {
                //If you don't own a spoon
                if (spoon.owner != this) {
                    //Enter Wait 1 Second
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }
                //If you own a spoon and are hungry, give it to your partner first to avoid deadlock (be modest)
                if (spouse.isHungry) {
                    System.out.println(name + ": Dear" + spouse.name + "You eat first");
                    spoon.setOwner(spouse);
                    continue;
                }

                //Eat it yourself
                spoon.use();
                isHungry = false;
                System.out.println(name + ": I'm full");
                //Give the spoon to each other after eating
                spoon.setOwner(spouse);
            }
        }
    }


    public static void main(String[] args) {
        Diner husband = new Diner("A cowboy");
        Diner wife = new Diner("Woman Weaver");

        Spoon spoon = new Spoon(husband);

        new Thread(new Runnable() {
            @Override
            public void run() {
                husband.eatWith(spoon, wife);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                wife.eatWith(spoon, husband);
            }
        }).start();
    }
}

Live locks appear when these programs run, and if you want to solve them, you just need to be modest about which step you occasionally introduce a random condition.

public void eatWith(Spoon spoon, Diner spouse) {
    while (isHungry) {
        //If you don't own a spoon
        if (spoon.owner != this) {
            //Enter Wait 1 Second
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            continue;
        }
        //If you own a spoon and are hungry, give it to your partner first to avoid deadlock (be modest)
        Random random = new Random();//Introducing random conditions to avoid active locks
        if (spouse.isHungry && random.nextInt(10) < 9) {
            System.out.println(name + ": Dear" + spouse.name + "You eat first");
            spoon.setOwner(spouse);
            continue;
        }

        //Eat it yourself
        spoon.use();
        isHungry = false;
        System.out.println(name + ": I'm full");
        //Give the spoon to each other after eating
        spoon.setOwner(spouse);
    }
}

hunger

Hunger is not summarized here, that is, some threads have been modest in letting other threads run, causing CPU resources to be unavailable all the time. Thread hunger can be solved by increasing thread priority.

Some interview questions

With regard to deadlocks, there are not many possible interview questions, but under what circumstances will deadlocks occur? What are the necessary conditions for a deadlock to occur? What are the ways to troubleshoot deadlocks? How do I avoid deadlocks?

summary

A brief summary of deadlocks

Tags: Java Back-end

Posted on Thu, 04 Nov 2021 13:58:57 -0400 by newbienewbie