Chapter 4: inter thread communication
4.1 waiting / notification mechanism
4.1.1 what is the waiting notification mechanism
In single thread programming, the operation to be executed needs to meet certain conditions, which can be placed in the if statement block. In multi-threaded programming, the conditions of thread a may not be met, but this situation is only temporary. Later, other thread B may update the conditions to meet the conditions of thread A. at this time, thread a can be suspended until its conditions are met, and then thread a can be awakened. The pseudo code of this step:
atomics{ // Atomic operation
While (the condition is not true){
wait for
}
After the conditions for the current thread to be awakened are met, continue to perform the following operations
}
4.1.2 implementation of waiting notification mechanism
The wait() method in the Object class can make the thread executing the current code wait and suspend execution until it is notified or interrupted. Before executing the wait() method, the thread must obtain a lock. Note:
① The wait() method can only be called by a lock object in a synchronized code block.
② After calling the wait() method, the current thread immediately releases the lock.
Pseudo code:
// Obtain the internal lock of the object before calling the wait() method
Synchronized (lock object){
While (the condition is not true){
// Calling the wait() method to pause the thread through the lock object will release the lock object
Lock object. wait();
}
// If the thread condition is met, continue to execute downward
}
The notify () method of the Object class must also be called by the lock object in the synchronous code block. The IllegalMonitorStateException exception is thrown without calling the object wait()/notify(). If there are multiple waiting threads, the notify () method can only wake one of them. Note: calling notify () in the synchronous code block. Method does not release the lock object immediately. The lock object will not be released until the current synchronization code block is executed. Generally, the notify() method is placed at the end of the synchronization code block. Pseudo code:
Synchronized (lock object){
// Execute the code that modifies the protection condition
// Wake up other threads
Lock object. notify();
}
public class Test{ public static void main(String[] args){ String lock = "abc";//Define a string as the lock object Thread t1 = new Thread(new Runnable(){ public void run(){ synchronized(lock){ System.out.println("Thread 1 started waiting" + System.currentTimeMillis()); try{ lock.wait();//When the thread waits, the lock object will be released, and the current thread will enter the blocked blocking state }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("Thread 1 end wait" + System.currentTimeMillis()); } } }); Thread t2 = new Thread(new Runnable(){ public void run(){ synchronized(lock){ System.out.println("Thread 2 starts waking up" + System.currentTimeMillis()); try{ lock.notify();//Wake up a thread waiting on the lock object }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("Thread 2 end wake" + System.currentTimeMillis()); } } }); t1.start(); Thread.sleep(3000);//Make sure t1 enters the waiting state t2.start(); } }
4.1.3 interrupt() method will interrupt wait()
When the thread is in the wait() waiting state, calling the interrupt() method of the thread object will interrupt the waiting state of the thread and generate an InterruptException.
public class Test{ public static void main(String[] args){ SubThread t = new SubThread; t.start(); Thread.sleep(2000); t.interrupt(); } } class SubThread extends Thread{ private static final Object LOCK = new Object();//Define constants as lock objects public void run(){ synchronized(LOCK){ try{ System.out.println("begin wait..."); LOCK.wait(); System.out,println("end wait"); }catch(InterruptedException e){ System.out.println("wait The waiting was interrupted"); } } } }
4.1.4 difference between notify() and notifyAll()
notify() can wake up only one thread at a time. If there are multiple waiting threads, it can wake up only one of them randomly; notifyAll() can wake up all waiting threads.
Calling notify() once can only wake up one thread, while other waiting threads are still waiting. For waiting threads, the notification signal is missed. This phenomenon is also called signal loss.
4.1.5 use of wait (long) method
wait(long) wait() with a long type parameter. If it is not awakened within the time specified by the parameter, it will wake up automatically after timeout.
4.1.6 premature notification
After the thread waits for wait(), it can call notify() to wake up the thread. If it wakes up too early, calling notify() before waiting may disrupt the normal running logic of the program.
4.1.7 wait conditions changed
When using the wait/notify mode, note that the wait conditions have changed, which may also cause logical confusion.
4.1.8 producer consumer model
In Java, the module responsible for generating data is the producer, and the module responsible for using data is the consumer. Producers and consumers solve the problem of data balance, that is, they have data before they can use it. When there is no data, consumers need to wait.
① Production consumption: operating value
public class Test{ public static void main(String[] args){ ValueOP obj = new ValueOP(); //Single producer, single consumer ProducerThread p = new ProducerThread(obj); ConsumerThread c = new ConsumerThread(obj); p.start(); c.start(); } } //Define thread classes to simulate producers class ProducerThread extends Thread{ private ValueOP obj; public ProducerThread(ValueOP obj){ this.obj = obj; } public void run(){ while(true){ obj.setValue(); } } } //Define thread classes to simulate consumers class ConsumerThread extends Thread{ private ValueOP obj; public ConsumerThread(ValueOP obj){ this.obj = obj; } public void run(){ while(true){ obj.getValue(); } } } //Define a class that operates on data class ValueOP{ private String value = ""; //Define a method to modify the value of the value field. If it is not an empty string, it will not be modified public void setValue(){ synchronized(this){ //Wait if the string is not empty while(!value.equalsIgnoreCase("")){ this.wait(); } //If value is an empty string, set the value of the value field String value = System.currentTimeMillis() + "-" + System.nanoTime(); System.out.println("set The set value is" + value); this.value = value; this.notify(); } } //Define the method to read the field. If it is an empty string, it will not be read public void getValue(){ synchronized(this){ //If the string is empty, wait while(value.equalsIngoreCase("")){ this.wait() } //If value is not an empty string, the value of the value field is read System.out.println("get The value of is" + this.value); this.value = ""; this.notify(); } } }
In the case of multiple producers and multiple consumers, if the notify() method is still used, the producer may wake up the producer or cancel
If the consumer wakes up and is in a suspended state, the notifyAll() method should be used.
② Production consumption: operation stack. It enables producers to store data in the List set, and consumers to get data from the List set and use it
List simulation stack.
public class Test{ public static void main(String[] args){ MyStacj stack = new MyStack(); //Multi producer, multi consumer ProducerThread p1 = new ProducerThread(stack); ProducerThread p2 = new ProducerThread(stack); ProducerThread p3 = new ProducerThread(stack); ConsumerThread c1 = new ConsumerThread(stack); ConsumerThread c2 = new ConsumerThread(stack); ConsumerThread c3 = new ConsumerThread(stack); p1.start(); p2.start(); p3.start(); c1.start(); c1.start(); c1.start(); } } //Define thread classes to simulate producers class ProducerThread extends Thread{ private MyStack stack; public ProducerThread(MyStack stack){ this.stack = stack; } public void run(){ while(true){ stack.push(); } } } //Define thread classes to simulate consumers class ConsumerThread extends Thread{ private MyStack stack; public ConsumerThread(MyStack stack){ this.stack = stack; } public void run(){ while(true){ stack.pop(); } } } class MyStack{ private List list = new ArrayList(); //Define set simulation stack private static final int MAX = 1; //Maximum capacity of the collection //Define method simulation stack public void push(){ synchronized(this){ //The data in the stack is full, unable to continue to enter the stack, wait while(list.size >= MAX){ System.out.println(Thread.currentThread.getName() + "begin wait..."); this.wait(); } String data = "data--" + Math.random(); System.out.println(Thread.currentThread.getName() + "Added data:" + data); list.add(data); this.notifyAll(); } } //Define method simulation stack public void push(){ synchronized(this){ //The data in the stack is empty, unable to obtain data, wait while(list.size == MAX){ System.out.println(Thread.currentThread.getName() + "begin wait..."); this.wait(); } System.out.println(Thread.currentThread.getName() + "Stack data:" + list.remove(0)); this.notifyAll(); } } }
4.2 communication between threads through pipeline
The PipeStream pipeline flow in the java.io package is used to transfer data between threads. One thread sends data to the output pipeline flow, and another thread reads data from the input pipeline.
Related classes include:
① PipedInputStream and PipedOutputStream: byte stream.
② PipedReader and PipedWriter: character stream.
public class Test{ public static void main(String[] args) throws IOException{ //Define pipe byte stream PipedInputStream in = new PipedInputStream; PipedOutputStream out = new PipedOutputStream; //Establishes a connection between the input pipe flow and the output pipe flow in.connect(out); //Create a thread to write data to the pipeline flow new Thread(new Runnable(){ public void run(){ writeData(out); } }).start(); //Create a thread to read data from the pipe stream new Thread(new Runnable(){ public void run(){ readData(in); } }).start(); } //Define a method to write data to the pipeline flow public static void writeData(PipedOutputStream out){ //Write the number between 0 and 100 into the pipeline respectively try{ for(int i = 0;i < 100;i++){ String data = "" + i; out.write(data.getBytes()); //Writes a byte array to the output pipeline stream } }catch(IOException e){ e.printStackTrace(); }finally{ out.close(); } } //Define a method to read data from a pipe stream public static void readData(PipedInputStream in){ //Read the numbers between 0 and 100 into the pipeline respectively try{ byte[] bytes = new byte[1024]; int len = in.read(bytes);//Returns the number of bytes read. If no data is read, - 1 is returned while(len != -1){ System.out.println(new String(bytes,0,len)); len = in.read(bytes);//After reading, continue reading data from the pipe } }catch(IOException e){ e.printStackTrace(); }finally{ in.close(); } } }
4.3 use of ThreadLocal
In addition to controlling the access of resources, you can also increase resources to ensure thread safety. ThreadLocal is mainly used to bind its own value for each thread.
//Basic use of ThreadLocal public class Test{ //Define ThreadLocal object static ThreadLocal threadLocal = new ThreadLocal(); //Define thread class static class SubThread extends Thread{ public void run(){ for(int i = 0;i < 20;i++){ //Sets the value associated with the thread threadLocal.set(Thread.currentThread().getName() + "-" + i); //Call the get method to read the associated value System.out.println(Thread.currentThread().getName() + " value = " + threadLocal.get()); } } } public static void main(String[] args){ SunThread t1 = new SubThread(); SunThread t2 = new SubThread(); t1.start(); t2.start(); } }
Save the variable value of the thread for each thread. i in the example is a local variable, which is unique to each thread.
ThreadLocal can specify an initial value.
//Example of setting initial value public class SubThreadLocal extends ThreadLocal<Date>{ //Override the initialValue method to set the initial value protected Date initialValue(){ return new Date(); //Set the current date as the initial value } } //Then directly use SunThreadLocal to create ThreadLocal objects. //static ThreadLocal threadLocal = new SubThreadLocal();
PS: sort out according to the power node course. If there is infringement, contact to delete it.