Threads execute concurrently, which appears to be random execution of threads. However, in practical applications, we use thread communication The execution order is required, which requires the use of thread communication
Thread communication Why not use priority to solve the running order of threads?
The overall priority is determined by the priority information in the thread pcb and the thread waiting time, so in general development, priority will not be relied upon to indicate the execution order of threads
See below Such a scene: a bakery example to describe the producer-consumer model
There is a bakery with bakers and customers, corresponding to our producers and consumers, and the bakery has an inventory for To store bread, when the inventory is full, it will no longer be produced. At the same time, consumers are also buying bread. When the inventory of bread is sold out, consumers must wait for new bread to be produced before they can continue to buy
Analysis: For what When to stop production and when to stop consumption, thread communication needs to be applied to accurately convey production and consumption information
wait(): let the object held by the current thread Release the lock and wait
wait(long timeout): The corresponding parameter is the time the thread waits
notify(): Wake up the thread that uses the same object to call wait to enter the waiting thread, and compete for the object lock again
notifyAll(): If there are multiple threads waiting, notifyAll wakes up all of them, notify wakes up one randomly
Note:
these The methods all belong to the methods in the Object class
Must be used in synchronized synchronized code blocks/synchronized methods
Which object is locked is the object to be used for wait and notify
call It does not wake up immediately after notify, but waits until synchronized is completed before waking up
After calling the wait method:
Make the thread executing the current code wait (the thread is placed in the waiting queue)
Release the current lock
Be awakened when certain conditions are met, and try to acquire the lock again
Conditions for the end of wait waiting:
Other threads call the notify method of the object
wait waiting time timeout (timeout parameter to specify the waiting time)
Other threads Calling the interrupted method causes wait to throw InterruptedException
When using the wait method without parameters, you need to use the notify method to wake up the thread and wait
This method is to wake up those threads waiting for the object lock of the object so that they can reacquire the object lock of the object
If there are multiple threads waiting, the thread scheduler will randomly select a wait Status thread (there is no first-come, first-served)
After the notify() method, the current thread will not release the object lock immediately. It will wait until the thread executing the notify() method finishes executing the program, that is, exiting the synchronization code The object lock will be released only after the block
This method has the same effect as the notify() method, except that when waking up, all waiting threads will be awakened
notify() method just randomly wakes up a thread
Prerequisite:
Yes 2 bakers, the baker can make two loaves at a time
The warehouse can store 100 loaves
There are 10 consumers, each consumer buys one loaf at a time
Note:
Consumption and production are carried out concurrently and in parallel, not one production and one consumption.
Implementation code:
public class Bakery { private static int total;//库存 public static void main(String[] args) { Producer producer = new Producer(); for(int i = 0;i < 2;i++){ new Thread(producer,"面包师傅-"+(i-1)).start(); } Consumer consumer = new Consumer(); for(int i = 0;i < 10;i++){ new Thread(consumer,"消费者-"+(i-1)).start(); } } private static class Producer implements Runnable{ private int num = 3; //生产者每次生产三个面包 @Override public void run() { try { while(true){ //一直生产 synchronized (Bakery.class){ while((total+num)>100){ //仓库满了,生产者等待 Bakery.class.wait(); } //等待解除 total += num; System.out.println(Thread.currentThread().getName()+"生产面包,库存:"+total); Thread.sleep(500); Bakery.class.notifyAll(); //唤醒生产 } Thread.sleep(500); } } catch (InterruptedException e) { e.printStackTrace(); } } } private static class Consumer implements Runnable{ private int num = 1; //消费者每次消费1个面包 @Override public void run() { try { while(true){ //一直消费 synchronized (Bakery.class){ while((total-num)<0){ //仓库空了,消费者等待 Bakery.class.wait(); } //解除消费者等待 total -= num; System.out.println(Thread.currentThread().getName()+"消费面包,库存:"+total); Thread.sleep(500); Bakery.class.notifyAll(); //唤醒消费 } Thread.sleep(500); } } catch (InterruptedException e) { e.printStackTrace(); } } } }
Partial printing results:
The blocking queue is a special queue that also follows the "first in, first out" principle. It is a thread-safe queue structure
Features: A typical producer-consumer model, generally used for task decoupling and peak reduction
When the queue is full, the queue will be blocked and waited. (Production), until other threads take elements from the queue
When the queue is empty, the queue will be blocked and wait (Consumption), until other threads insert elements into the queue
The producer-consumer model uses a container to solve the problem of strong coupling between producers and consumers
Producers and consumers do not communicate directly with each other, but through blocking The queue is used for communication, so after the producer produces the data, it waits for the consumer to process it and throws it directly to the blocking queue. The consumer does not ask the producer for data, but directly takes it from the blocking queue.
The blocking queue is equivalent In a buffer, it balances the processing capabilities of producers and consumers
Blocking queue can also decouple producers and consumers
The implementation of the above bakery business is the producer-consumer model An example of
The blocking queue is built into the Java standard library. If we need to use the blocking queue in some programs, directly use the blocking queue in the standard library. That’s it
BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue
put 方法用于阻塞式的入队列, take 用于阻塞式的出队列
BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性
BlockingDeque<String> queue = new LinkedBlockingDeque<>(); queue.put("hello"); //如果队列为空,直接出出队列就会阻塞 String ret = queue.take(); System.out.println(ret);
这里使用数组实现一个循环队列来模拟阻塞队列
当队列为空的时候,就不能取元素了,就进入wait等待,当有元素存放时,唤醒
当队列为满的时候,就不能存元素了,就进入wait等待,当铀元素取出时,唤醒
实现代码:
public class MyBlockingQueue { //使用数组实现一个循环队列,队列里面存放的是线程要执行的任务 private Runnable[] tasks; //队列中任务的数量,根据数量来判断是否可以存取 private int count; private int putIndex; //存放任务位置 private int takeIndex; //取出任务位置 //有参的构造方法,表示队列容量 public MyBlockingQueue(int size){ tasks = new Runnable[size]; } //存任务 public void put(Runnable task){ try { synchronized (MyBlockingQueue.class){ //如果队列容量满了,则存任务等待 while(count == tasks.length){ MyBlockingQueue.class.wait(); } tasks[putIndex] = task; //将任务放入数组 putIndex = (putIndex+1) % tasks.length; //更新存任务位置 count++; //更新存放数量 MyBlockingQueue.class.notifyAll(); //唤醒存任务 } } catch (InterruptedException e) { e.printStackTrace(); } } //取任务 public Runnable take(){ try { synchronized (MyBlockingQueue.class){ //如果队列任务为空,则取任务等待 while(count==0){ MyBlockingQueue.class.wait(); } //取任务 Runnable task = tasks[takeIndex]; takeIndex = (takeIndex+1) % tasks.length; //更新取任务位置 count--; //更新存放数量 MyBlockingQueue.class.notifyAll(); //唤醒取任务 return task; } } catch (InterruptedException e) { throw new RuntimeException("存放任务出错",e); } } }
相同点:
都可以让线程放弃执行一段时间
不同点:
☘️wait用于线程通信,让线程在等待队列中等待
☘️sleep让线程阻塞一段时间,阻塞在阻塞队列中
☘️wait需要搭配synchronized使用,sleep不用搭配
☘️wait是Object类的方法,sleep是Thread的静态方法
The above is the detailed content of How to use wait and notify to implement communication between threads in Java. For more information, please follow other related articles on the PHP Chinese website!