线程通信

181 阅读3分钟

线程间通信

线程间通信会有一个存储任务的队列,队列有以下三种状况

  • 队列为空,会通知消费者等待,
  • 队列满时,会通知生产者等待
  • 队列有任务但不为空,消费者正常运行,生产者正常运行

单线程通信(一个生产者一个消费者)

使用Object中的方法,wait()notify()来实现

wait()

  • wait()notify()必须获取对象,所以两个方法都需要在同步方法中使用(synchroized)
  • 当前线程执行了该对象的wait方法之后,将会放弃对该monitor的所有权并且进入与该对象关联的wait set中,也就是说,一旦当前线程执行了某个objectwait方法之后,它就会释放对该对象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的事情