线程间通信
线程间通信会有一个存储任务的队列,队列有以下三种状况
- 队列为空,会通知消费者等待,
- 队列满时,会通知生产者等待
- 队列有任务但不为空,消费者正常运行,生产者正常运行
单线程通信(一个生产者一个消费者)
使用
Object中的方法,wait()和notify()来实现
wait()
wait()和notify()必须获取对象,所以两个方法都需要在同步方法中使用(synchroized)- 当前线程执行了该对象的
wait方法之后,将会放弃对该monitor的所有权并且进入与该对象关联的wait set中,也就是说,一旦当前线程执行了某个object的wait方法之后,它就会释放对该对象monitor的所有权,其他线程也会有机会继续争夺该monitor的所有权
notify()
notify()会唤醒单个正在执行该对象的wait方法的线程- 唤醒的线程需要重新获取对该对象所关联monitor的lock才能执行
import java.util.LinkedList;
/**
* EventQuque 任务队列,提供添加任务方法,消费方法.
* @author husky
* @date 2019/7/12 11:21
*/
public class EventQueue {
private final int max;
static class Event{
}
private final LinkedList<Event> events = new LinkedList<>();
private final static Integer DEDAULT_MAX_EVENT = 10;
public EventQueue(int max) {
this.max = max;
}
public EventQueue(){
this(DEDAULT_MAX_EVENT);
}
public void offer(Event event){
synchronized (events){
if(events.size() >= DEDAULT_MAX_EVENT){
try {
console("the event list is full,wait consumer consume");
events.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
console("the event is submit");
events.addLast(event);
events.notify();
}
}
public void take(){
synchronized (events){
if(events.size() <= 0){
try {
console("the event list is empty,wait producer produce");
events.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Event eventFirst = events.getFirst();
events.removeFirst();
console("Event is " + eventFirst + "handler");
events.notify();
}
}
public static void console(String msg){
System.out.println(msg);
}
}
import java.util.concurrent.TimeUnit;
/**
* EventClient 使用
* @author husky
* @date 2019/7/12 11:44
*/
public class EventClient {
public static void main(String[] args) {
final EventQueue eventQueue = new EventQueue();
new Thread(()->{
for(;;){
eventQueue.offer(new EventQueue.Event());
}
},"producer").start();
new Thread(()->{
for(;;){
eventQueue.take();
//模拟消费event消耗时间
try {
TimeUnit.MICROSECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"consumer").start();
}
多线程通信(多生成者多消费者)
notifyAll()方法可以唤醒由于调用wait()方法阻塞的线程,notify()可以唤醒起一个线程,而notifyAll()是唤醒所有阻塞的线程,所有线程需要竞争monitor的锁.
import java.util.LinkedList;
/**
* EventQueue 任务队列
* @author husky
* @date 2019/7/12 14:17
* 多线程间通信
*/
public class EventQueue {
private final int max;
static class Event{
}
private final LinkedList<Event> events = new LinkedList<>();
private final static Integer DEDAULT_MAX_EVENT = 10;
public EventQueue(int max) {
this.max = max;
}
public EventQueue(){
this(DEDAULT_MAX_EVENT);
}
public void offer(Event event){
synchronized (events){
while(events.size() >= DEDAULT_MAX_EVENT){
try {
console("the event list is full,wait consumer consume");
events.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
console(Thread.currentThread().getName()+"the event is submit");
events.addLast(event);
events.notifyAll();
}
}
public void take(){
synchronized (events){
while(events.size() <= 0){
try {
console("the event list is empty,wait producer produce");
events.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Event eventFirst = events.getFirst();
events.removeFirst();
console(Thread.currentThread().getName()+"Event is " + eventFirst + "handler");
events.notifyAll();
}
}
public static void console(String msg){
System.out.println(msg);
}
}
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
/**
* @author husky
* @date 2019/7/12 14:19
*/
public class EventClient {
public static void main(String[] args) {
final EventQueue eventQueue = new EventQueue();
IntStream.rangeClosed(0,2).mapToObj(i->new Thread("producer-"+i){
@Override
public void run() {
for(;;) {
eventQueue.offer(new EventQueue.Event());
}
}
}).forEach(Thread::start);
IntStream.rangeClosed(0,3).mapToObj(i->new Thread("consumer-"+i){
@Override
public void run() {
for(;;){
eventQueue.take();
try {
TimeUnit.MICROSECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).forEach(Thread::start);
}
}
其中需要解释的是,为什么单线程通信在判断是否阻塞时,使用if,而多线程判断使用while.因为多个在一起时,有可能多个同时阻塞,被唤醒时,如果用if的话,很多条件并没有再次判断.
而使用while可以在执行
wait()之后再次判断条件是否正确,这里也就牵扯到wait set的事情