Java多线程入门9-线程通信和生产者消费者问题

一、线程通信

  多线程各自独立运行,不可避免的,会遇到线程间互通消息的需求,即线程通信。

  本篇只是多线程的入门,认识线程间通信的两个基本办法。更多的线程通信方法与这两个基本方法原理类似,并在JUC并发编程系列中介绍。

  本系列第一篇中已经介绍到,线程是在进程中生成的,线程间的通信比进程间通信方便,开销也更小。线程无需另外建立线程间的连接,通过共享进程资源,即可进行通信。

  线程通信基本方法:

  1、管程法。即设立一个共享缓冲区存放通信数据

  2、信号灯法。类似交通灯,即设立一个共享标志位,通过标志位传递状态信息

二、生产者消费者

  下面通过分析生产者消费者经典问题,来帮助学习线程通信。

  有这么一个场景,仓库中存放产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。需要满足下面条件。

  • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止。
  • 如果仓库中有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。
  • 当仓库满时,生产者需要立即停止生产,不能超量。
  • 当仓库空时,消费者需要立即停止消费,不能产生负数。

  显然,这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。
Java多线程入门9-线程通信和生产者消费者问题_第1张图片

  synchronized关键字可以实现同步,但没办法实现不同线程之间的消息传递

  回忆前面学习到的线程方法,wait()和notifyAll()可以满足需求。

  wait和notify是一个系列的方法,都是属于对象Object的方法,不是属于线程的。它们用在线程同步方法或同步块中。

  • wait():obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout)timeout时间到自动唤醒。

  • notify():obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。

  • notifyAll():obj.notifyAll()唤醒在此对象监视器上等待的所有线程。

三、代码实现

  管程法,也就是缓冲区实现线程通信:

// 生产者类
class Producer implements Runnable {
    Store store;
    Producer(Store store) {
        this.store = store;
    }

    @Override
    public void run() {
        // 循环生产
        for(int i = 1; i < 20; i++) {
            store.push();
        }
    }
}

// 消费者类
class Consumer implements Runnable {
    Store store;
    Consumer(Store store) {
        this.store = store;
    }

    @Override
    public void run() {
        // 循环消费
        for(int i = 1; i < 20; i++) {
            store.pop();
        }
    }
}

// 产品
class Product { }

// 仓库
class Store {
    // 仓库容量
    int count = 5;
    // 仓库容器
    List<Product> bucket = new ArrayList<>();

    // 生产产品
    public synchronized void push() {
        // 仓库未满
        if(bucket.size() < count) {
            bucket.add(new Product());
            System.out.println("生产者新增一个产品,仓库位置" + bucket.size());
        }
        // 仓库满
        else {
            System.out.println("仓库已满");
            try {
                // 唤醒其他线程
                this.notifyAll();
                // 生产者等待
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 消费产品
    public synchronized void pop() {
        // 仓库非空
        if(bucket.size() > 0) {
            System.out.println("消费者用掉一个产品,仓库位置" + bucket.size());
            bucket.remove(bucket.size() - 1);
        }
        // 仓库已空
        else {
            System.out.println("仓库已空");
            try {
                // 唤醒其他线程
                this.notifyAll();
                // 消费者等待
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

  执行:

public static void main(String[] args) {
    // 新建仓库类
    Store store = new Store();
    Producer p = new Producer(store);
    Consumer c= new Consumer(store);
    // 启动生产者线程
    new Thread(p).start();
    // 启动消费者线程
    new Thread(c).start();
}

  输出:

Java多线程入门9-线程通信和生产者消费者问题_第2张图片

  类似的,信号灯法,只需要将上面示例代码中的缓冲区改成标志位即可。

你可能感兴趣的:(多线程,java,开发语言,后端,多线程)