Java Self-Learning-Multithreaded Interaction
Interactive wait and notify between Java threads
There is a need for interactive notifications between threads, considering the following:
There are two threads dealing with the same hero.
One for blood and one for blood reduction.
Threads that reduce blood, find blood = 1, stop bleeding until the threads that add blood for heroes, can continue to reduce blood
Step 1: Bad solution
Deliberately designed blood-reducing threads are more frequent and Galen's blood will reach 1 sooner or later
The while loop is used in the blood-reducing thread to determine if 1 is a 1 and if it is a 1, it will continue to circulate until the blood-adding thread responds to the volume
This is a bad solution because it will consume a lot of CPU and slow down performance
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 Now*** %s, %s Blood becomes %.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; }
}
.
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){ //Galen's blood will reach 1 sooner or later because he will lose blood faster //Use the while loop to determine if 1 is a 1 and if 1 is a 1, it keeps looping //Until the Bloodfeeding thread responds while(gareen.hp==1){ continue; } gareen.hurt(); System.out.printf("t1 by%s Decrease blood 1 point,After reducing blood,%s The blood volume is%.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 point 1,After increasing blood,%s The blood volume is%.0f%n",gareen.name,gareen.name,gareen.hp); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; t2.start(); }
}
Step 2: Use wait and notify for thread interaction
In the Hero class: hurt() method of blood reduction: when hp=1, execute this.wait().
this.wait() means to let the thread holding this hold wait and temporarily release the hold
Threads that enter the hurt method must be blood-reducing threads, and this.wait() will temporarily release their possession of this.This gives the Bloodfeeding thread the opportunity to enter the recover() Bloodfeeding method.
Reco() blood method: increase blood volume, perform this.notify();
this.notify() tells threads waiting at this that they can wake up.The thread waiting on this is the blood-reducing thread.Once recover() finishes, the blood-feeding thread releases this, and the blood-reducing thread can re-occupy this and perform the subsequent blood-reducing work.
Use wait and notify for thread interaction
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 point 1,After increasing blood,%s The blood volume is%.0f%n", name, name, hp); // Notify threads waiting on this object that they can wake up, such as Line 20, the waiting blood-reducing thread this.notify(); } public synchronized void hurt() { if (hp == 1) { try { // Let the haemolytic thread that owns 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 Decrease blood 1 point,After reducing blood,%s The blood volume is%.0f%n", name, name, hp); } public void attackHero(Hero h) { h.hp -= damage; System.out.format("%s Now*** %s, %s Blood becomes %.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; }
}
.
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){ //No need for circular judgment
// while(gareen.hp==1){
// continue;
// }
gareen.hurt(); 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(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; t2.start(); }
}
Step 3: About wait, notify, and notifyAll
Notice what the two methods, wait() and notify(), are on?
public synchronized void hurt() {
. . .
this.wait();
. . .
}
public synchronized void recover() {
. . .
this.notify();
}
It should be emphasized here that the wait and notify methods are not methods on the Thread thread; they are methods on the Object.
Because all Object s can be used as synchronization objects, wait and notify are precisely methods on synchronization objects.
What wait() means is to let the thread that occupies this synchronization object temporarily release the current occupancy and wait.So a call to wait is a precondition, it must be in the synchronized block, or an error will occur.
Notfy () means to notify a thread waiting on this synchronized object that you can wake up and have a chance to occupy the current object again.
NotfyAll () means to notify all threads waiting on this synchronized object that you can wake up and have a chance to occupy the current object again.
Exercise: Producer-consumer questions
The producer-consumer problem is a very typical thread interaction problem.
Use stacks to store data
1.1 Modify stack to support thread security
1.2 Handles the boundary operations of the stack, and when the data in the stack is 0, the thread accessing the pull waits.When the data in the stack is 200, the thread accessing push waits
Provides a Producer thread class that produces random uppercase characters pushed onto the stack
Provides a Consumer thread class that pops characters from the stack and prints them to the console
Provide a test class that allows two producers and three consumer threads to run simultaneously with similar results as follows:
Insert a picture description here
Answer:
MyStack.java
package multiplethread;
import java.util.ArrayList;
import java.util.LinkedList;
public class MyStack<T> {
LinkedList<T> values = new LinkedList<T>(); public synchronized void push(T t) { while(values.size()>=200){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } this.notifyAll(); values.addLast(t); } public synchronized T pull() { while(values.isEmpty()){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } this.notifyAll(); return values.removeLast(); } public T peek() { return values.getLast(); }
}
ProducerThread.java
package multiplethread;
public class ProducerThread extends Thread{
private MyStack<Character> stack; public ProducerThread(MyStack<Character> stack,String name){ super(name); this.stack =stack; } public void run(){ while(true){ char c = randomChar(); System.out.println(this.getName()+" Press in: " + c); stack.push(c); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public char randomChar(){ return (char) (Math.random()*('Z'+1-'A') + 'A'); }
}
ConsumerThread.java
package multiplethread;
public class ConsumerThread extends Thread{
private MyStack<Character> stack; public ConsumerThread(MyStack<Character> stack,String name){ super(name); this.stack =stack; } public void run(){ while(true){ char c = stack.pull(); System.out.println(this.getName()+" Eject: " + c); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public char randomChar(){ return (char) (Math.random()*('Z'+1-'A') + 'A'); }
}
TestThread.java
package multiplethrea
public class TestThread {
public static void main(String[] args) { MyStack<Character> stack = new MyStack<>(); new ProducerThread(stack, "Producer1").start(); new ProducerThread(stack, "Producer2").start(); new ConsumerThread(stack, "Consumer1").start(); new ConsumerThread(stack, "Consumer2").start(); new ConsumerThread(stack, "Consumer3").start(); }
}