In multithreading development, there is one of the most classic operation cases, that is, the producer consumer. The producer keeps producing the product and the consumer keeps taking the product.
1, Review of key points of multithreading
1.1 what is a process?
A program is an ordered collection of instructions and data. It has no meaning of running and is a static concept. Process is a process of program execution on the processor, which is a dynamic concept.
Process is a program with certain independent functions, an entity, each process has its own address space.
1.2 status of process
A running process has three basic states:
- Ready status (ready)
- Running status
- Blocked status
1.3 thread implementation
Implementation mode 1: inherit Thread class
class MyThread extends Thread{ public void run(){ //Logic processing } } MyThread mt = new MyThread(); mt.start();
Implementation mode 2: implement the Runnable interface
class MyRunnable implements Runnable{ public void run(){ //Logic processing } } MyRunnable mr = new MyRunnable(); Thread t = new Thread(mr); t.start();
1.4 thread status
- New (initialization state)
- Runnable (runnable)
- Blocked (blocking state)
- Dead (end state)
Note one difference:
Thread sleep: the sleep method is used to pause (temporarily stop execution) the currently executing thread for a specified number of milliseconds, and release the time slice of CPU, which depends on the accuracy and accuracy of the system timer and scheduler. Threads do not lose ownership of any displays.
Thread waiting: use the wait method to temporarily stop the execution of the thread, release the time slice of the CPU, release the ownership of the object monitor, and wait for other threads to wake up through the notify method.
2, Analysis of producer and consumer cases
There is a chef and a waiter in the hotel. The waiter must wait for the chef to prepare the meal. When the chef is ready, he will inform the waiter, then the waiter will serve, and then return to wait.
This is an example of task collaboration where the chef represents the producer and the waiter represents the consumer.
//Food (data objects that two threads need to share) class Food{ private String name; //Dish name private String description; //The efficacy of vegetables private float price; //Unit Price private boolean flag = true; //true means it can be produced, false means it can be consumed public Food() { } public Food(String name, String description, float price) { this.name = name; this.description = description; this.price = price; } /** * Food production * @param name * @param description * @param price * Synchronization: object lock, marking a lock on the current object */ public synchronized void set(String name,String description, float price){ //The current thread cannot produce food if(!flag){ try { this.wait(); //Thread enters wait state, freeing ownership of object monitor (object lock) } catch (InterruptedException e) { e.printStackTrace(); } } this.setName(name); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } this.setDescription(description); this.setPrice(price); flag = false;//It means it's ready for consumption this.notify();//Wake up another thread waiting } /** * Consuming food */ public synchronized void get(){ //No consumption if(flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.getName()+"-"+this.getDescription()+"-"+this.getPrice()); flag = true; //It's ready for production this.notify(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } @Override public String toString() { return "Food{" + "name='" + name + '\'' + ", description='" + description + '\'' + ", price=" + price + '}'; } }
//Producer object class Producer implements Runnable{ private Food food; public Producer(Food food) { this.food = food; } @Override public void run() { for (int i=0;i<20;i++){ if (i%2==0){ food.set("Stewed egg with leek","Programmer's favorite, Buah",50f); }else{ food.set("Cold kidney treasure","Man's favorite, Dabu",200f); } } } }
//Consumer object class Consumer implements Runnable{ private Food food; public Consumer(Food food) { this.food = food; } @Override public void run() { for (int i=0;i<20;i++){ food.get(); } } }
public class ProducterConsumerDemo { public static void main(String[] args) { Food food = new Food(); Producer p = new Producer(food); Consumer c = new Consumer(food); Thread tp = new Thread(p); Thread cp = new Thread(c); tp.start(); //It is not a startup thread, which means that the current thread is ready and waiting for CPU scheduling at any time cp.start(); } }
The test results are as follows:
3, Multithreaded interview questions
1. What is the role of producer consumer model
- The most important role of producer consumer model is to improve the operation efficiency of the whole system by balancing the production capacity of producers and the consumption capacity of consumers
- Decoupling, which is a side effect of producer consumer model, means that there are few connections between producers and consumers, and the less connections, the more independent development without mutual restriction
2. What's the difference between sleep method and wait method
- Using the sleep method, the currently executing thread is suspended (temporarily stopped) in a specified number of milliseconds, and the time slice of CPU is released, which depends on the precision and accuracy of the system timer and scheduler. Threads do not lose ownership of any displays.
- Using the wait method, the thread is temporarily stopped, the CPU time slice is released, and the ownership of the object monitor is released, waiting for other threads to wake up through the notify method.
3. How to share data between two threads
-
Method 1: abstract the data into a class, and take the operation of the data as the method of the class, so that the design can be easily synchronized, as long as the method is added with synchronized
-
Method 2: take the Runnable object as an internal class of a class, and the shared data as a member variable of this class. The operation methods of each thread for the shared data are also encapsulated in the external class, so as to realize the synchronization and mutual exclusion of each operation of the data. As the Runnable object of the internal class, call these methods of the external class.
4. Why wait() and notify()/notifyAll() methods are called in the synchronization block
- Understand that each object can be considered a "monitor", which consists of three parts (an exclusive lock, an entry queue, and a waiting queue). Note that an object can only have one exclusive lock, but any thread can have it.
- For an object's asynchronous method, it can be called by any thread at any time. (that is, common methods can be called by multiple threads at the same time)
- For the synchronization method of an object, only the exclusive lock of the object can call the synchronization method. If the exclusive lock is occupied by another thread, the other thread calling the synchronization method will be blocked.
- If a thread with the exclusive lock calls the wait() method of the object synchronization method, the thread will release the exclusive lock and join the waiting queue of the object;
- A thread calls the notify() notifyall() method to transfer the threads waiting for the queue to the entry queue and let them compete for the lock.
5. What's the difference between the wait() method and the notify()/notifyAll() method when abandoning the object monitor
- wait(): causes the current thread to wait until other threads call the notify() method or notifyAll() method of this object or the specified event runs out
- notify(): wake up a single thread waiting on this object monitor
- notifyAll(): wakes up all threads waiting on this object monitor
- wait(), notify/notifyAll() methods are local final methods of Object and cannot be overridden.
- The wait() and notify/notifyAll methods must be used in synchronization blocks.
- Because wait() and notify/notifyAll() are placed in the synchronization code block, the thread must enter the critical area when executing them, that is, the thread must have obtained the lock.
- When the thread executes wait(), it will release the current lock, then release the CPU and enter the waiting state.
- When the notify/notifyAll method is executed, a thread waiting for the lock of the object will wake up, and then continue to execute until the locked area of the object (synchronized decorated code block) is completed before releasing the lock.
- If the corresponding object lock is not obtained before executing wait() and notify/notifyAll(), an exception of java.lang.IllegalMonitorStateException will be thrown.
6. What is the function of Thread.sleep(0)
- It is equivalent to yield(), which changes the current thread from the running state to the ready state. The CPU will reselect the thread to execute, and the just thread may be selected.