358. Java intermediate 13 - [multithreading deadlock, interaction] June 26, 2020

Keywords: Java

0. Directory

1. Deadlock

When the business is complex, deadlock may occur in multithreaded applications

  1. Thread 1 first takes object 1, then tries to take object 2
  2. Thread 2 first takes object 2, then tries to take object 1
  3. Thread 1 waits for thread 2 to release object 2
  4. Meanwhile, thread 2 waits for thread 1 to release object 1
    Will be... Keep waiting until the world is old, the sea is dry and the rocks are rotten, the mountains are free of edges, the heaven and the earth meet...
package multiplethread;
   
import charactor.Hero;
    
public class TestThread {
      
    public static void main(String[] args) {
        final Hero ahri = new Hero();
        ahri.name = "Nine Tailed Fox";
        final Hero annie = new Hero();
        annie.name = "Annie";
         
        Thread t1 = new Thread(){
            public void run(){
                //Possession of nine foxes
                synchronized (ahri) {
                    System.out.println("t1 Possessed nine foxes");
                    try {
                        //Pause for 1000 milliseconds, another thread has enough time to occupy Annie
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                     
                    System.out.println("t1 Trying to take Annie");
                    System.out.println("t1 Waiting....");
                    synchronized (annie) {
                        System.out.println("do something");
                    }
                }  
                 
            }
        };
        t1.start();
        Thread t2 = new Thread(){
            public void run(){
                //Take Annie
                synchronized (annie) {
                    System.out.println("t2 Annie's in possession");
                    try {
                         
                        //Pause for 1000 milliseconds, another thread has enough time to occupy nine goblins temporarily
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println("t2 Trying to possess nine foxes");
                    System.out.println("t2 Waiting....");
                    synchronized (ahri) {
                        System.out.println("do something");
                    }
                }  
                 
            }
        };
        t2.start();
   }
        
}

2. Interaction between JAVA threads WAIT and NOTIFY

There is a need for interactive notification between threads. Consider the following situations:
There are two threads that deal with the same hero.
One adds blood, one reduces blood.

The thread of blood reduction finds that the blood volume = 1, so it stops blood reduction. Until the thread of blood addition adds blood to the hero, it can continue to reduce blood

3. Bad solution

If the thread frequency of blood reduction is designed to be higher, Galen's blood volume will reach 1 sooner or later
The while loop is used in the blood reducing thread to determine whether it is 1. If it is 1, the loop will continue until the blood adding thread recovers the blood volume
This is a bad solution, because it will take up a lot of CPU and slow down performance

  • Hero.java
package charactor;
   
public class Hero{
    public String name;
    public float hp;
      
    public int damage;
      
    public synchronized void recover(){
        hp=hp+1;
    }    
 
    public synchronized void hurt(){
            hp=hp-1;   
    }
      
    public void attackHero(Hero h) {
        h.hp-=damage;
        System.out.format("%s Attacking %s, %s The blood of %.0f%n",name,h.name,h.name,h.hp);
        if(h.isDead())
            System.out.println(h.name +"Dead!");
    }
   
    public boolean isDead() {
        return 0>=hp?true:false;
    }
   
}
  • TestThread.java
package multiplethread;
    
import java.awt.GradientPaint;
  
import charactor.Hero;
    
public class TestThread {
    
    public static void main(String[] args) {
  
        final Hero gareen = new Hero();
        gareen.name = "Galen";
        gareen.hp = 616;
           
        Thread t1 = new Thread(){
            public void run(){
                while(true){
                     
                    //Because it's faster to lose blood, Galen's blood volume will reach 1 sooner or later
                    //Use the while loop to determine whether it is 1, and if it is 1, it will continue to loop
                    //Until the blood adding thread recovers the blood volume
                    while(gareen.hp==1){
                        continue;
                    }
                     
                    gareen.hurt();
                    System.out.printf("t1 by%s Blood reduction 1 point,After reducing blood,%s The blood volume of%.0f%n",gareen.name,gareen.name,gareen.hp);
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
 
            }
        };
        t1.start();
 
        Thread t2 = new Thread(){
            public void run(){
                while(true){
                    gareen.recover();
                    System.out.printf("t2 by%s Return blood 1 point,After increasing blood,%s The blood volume of%.0f%n",gareen.name,gareen.name,gareen.hp);
 
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
 
            }
        };
        t2.start();
           
    }
        
}

4. Using wait and notify for thread interaction

In Hero class: hurt() blood reduction method: when hp=1, execute this.wait().
this.wait(): indicates to let the thread occupying this wait and temporarily release the occupation
The thread that enters the hurt method must be a blood reducing thread, this.wait() will cause the thread to temporarily release its possession of this. In this way, the blooding thread will have a chance to enter the recover() blooding method.

recover() add blood method: increased blood volume, execute this.notify();
this.notify() indicates that the thread waiting for this can wake up. The thread waiting for this is exactly the thread of blood reduction. Once recover() is over, the blood adding thread releases this, and the blood reducing thread can occupy this again and perform the subsequent blood reducing work.

  • Hero.java
package charactor;
 
public class Hero {
    public String name;
    public float hp;
 
    public int damage;
 
    public synchronized void recover() {
        hp = hp + 1;
        System.out.printf("%s Return blood 1 point,After increasing blood,%s The blood volume of%.0f%n", name, name, hp);
        // Notify those threads waiting on this object that they can wake up, such as line 20, waiting for the blood reducing thread to wake up
        this.notify();
    }
 
    public synchronized void hurt() {
        if (hp == 1) {
            try {
                // Let the blood reducing thread occupy this temporarily release the possession of this and wait
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
 
        hp = hp - 1;
        System.out.printf("%s Blood reduction 1 point,After reducing blood,%s The blood volume of%.0f%n", name, name, hp);
    }
 
    public void attackHero(Hero h) {
        h.hp -= damage;
        System.out.format("%s Attacking %s, %s The blood of %.0f%n", name, h.name, h.name, h.hp);
        if (h.isDead())
            System.out.println(h.name + "Dead!");
    }
 
    public boolean isDead() {
        return 0 >= hp ? true : false;
    }
 
}
  • TestThread.java
package charactor;
 
public class Hero {
    public String name;
    public float hp;
 
    public int damage;
 
    public synchronized void recover() {
        hp = hp + 1;
        System.out.printf("%s Return blood 1 point,After increasing blood,%s The blood volume of%.0f%n", name, name, hp);
        // Notify those threads waiting on this object that they can wake up, such as line 20, waiting for the blood reducing thread to wake up
        this.notify();
    }
 
    public synchronized void hurt() {
        if (hp == 1) {
            try {
                // Let the blood reducing thread occupy this temporarily release the possession of this and wait
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
 
        hp = hp - 1;
        System.out.printf("%s Blood reduction 1 point,After reducing blood,%s The blood volume of%.0f%n", name, name, hp);
    }
 
    public void attackHero(Hero h) {
        h.hp -= damage;
        System.out.format("%s Attacking %s, %s The blood of %.0f%n", name, h.name, h.name, h.hp);
        if (h.isDead())
            System.out.println(h.name + "Dead!");
    }
 
    public boolean isDead() {
        return 0 >= hp ? true : false;
    }
 
}

5. About wait, notify, and notifyAll

Notice what the wait() and notify() methods are on?

public synchronized void hurt() {
  . . . 
  this.wait();
  . . . 
}
 

 
public synchronized void recover() {
   . . . 
   this.notify();
}

It should be emphasized that wait method and notify method are not methods on Thread thread Thread, but methods on Object.

Because all objects can be used as synchronization objects, wait and notify are exactly methods on synchronization objects.

wait() means: let the thread occupying the synchronization object temporarily release the current occupation and wait. Therefore, there are preconditions for calling wait, which must be in the synchronized block, otherwise an error will occur.

notify() means to inform a thread waiting on the synchronization object that you can wake up and have a chance to occupy the current object again.

notifyAll() means to inform all threads waiting on the synchronization object that you can wake up and have a chance to occupy the current object again.

6. Reference link

[01] How2j - multithreading - deadlock
[02] How2j - multithreading - interaction

Posted by TheLoveableMonty on Fri, 26 Jun 2020 01:06:16 -0700