线程的基本协作和生产者消费者

Posted jackpn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程的基本协作和生产者消费者相关的知识,希望对你有一定的参考价值。

协作基础(wait/notify)

Java的根父类是Object,Java在Object类而非Thread类中,定义了一些线程协作的基本方法,使得每个对象都可以调用这些方法,这些方法有两类,一类是wait,另一类是notify。

wait方法主要有两个:

public final void wait() throws InterruptedException
public final native void wait(long timeout) throws InterruptedException;

一个带时间参数,单位是毫秒,表示最多等待这么长时间,参数为0表示无限期等待。一个不带时间参数,表示无限期等待,实际就是调用wait(0)。在等待期间都可以被中断,如果被中断,会抛出InterruptedException。

wait实际上做了什么呢?每个对象都有一把锁和一个锁等待队列,一个线程在进入synchronized代码块时,会尝试获取锁,获取不到的话会把当前线程加入等待队列中。其实,除了用于锁的等待队列,每个对象还有另一个等待队列,表示条件队列,该队列用于线程间的协作。调用wait就会把当前线程放到条件队列上并阻塞,表示当前线程执行不下去了,它需要等待一个条件,这个条件它自己改变不了,需要其他线程改变。当其他线程改变了条件后,应该调用Object的notify方法:

public final native void notify();
public final native void notifyAll();

notify做的事情就是从条件队列中选一个线程,将其从队列中移除并唤醒,notifyAll和notify的区别是,它会移除条件队列中所有的线程并全部唤醒。

wait/notify方法只能在synchronized代码块内被调用,如果调用wait/notify方法时,当前线程没有持有对象锁,会抛出异常java.lang.IllegalMonitorStateException。

wait的具体过程是:

    1. 把当前线程放入条件等待队列,释放对象锁,阻塞等待,线程状态变为WAITING或TIMED_WAITING
    2. 等待时间到或被其他线程调用notify/notifyAll从条件队列中移除,这时,要重新竞争对象锁
      • 如果能够获得锁,线程状态变为RUNNABLE,并从wait调用中返回
      • 否则,该线程加入对象锁等待队列,线程状态变为BLOCKED,只有在获得锁后才会从wait调用中返回

线程从wait调用中返回后,不代表其等待的条件就一定成立了,它需要重新检查其等待的条件,一般的调用模式是:

synchronized (obj) {
    while (条件不成立)
        obj.wait();
    ... // 条件满足后的操作
}

 

生产者/消费者模式

下面来看一个生产者和消费者的例子:

/**
 * @author 沉默哥
 * 
 */
public class MyProducerConsumerDemo {
  static class GoodsQueue {
    private int size;
    private Queue<String> que = new ArrayDeque<String>();

    public GoodsQueue(int size) {// 维护一个有界队列,传入队列的最大容量
      super();
      this.size = size;
    }

    public synchronized void put(String e) throws InterruptedException {
      while (que.size() == size) {
        System.out.println("队列已满,生产者等待");
        wait();
      }
      que.add(e);
      System.out.println("生产者生产:" + e);
      notify();
    }

    public synchronized String take() throws InterruptedException {
      while (que.size() == 0) {
        System.out.println("队列为空,消费者等待");
        wait();
      }
      String e = que.poll();
      System.out.println("消费者消费" + e);
      notify();
      return e;
    }
  }

  static class Producer extends Thread {
    GoodsQueue que;
    Random rad = new Random();

    public Producer(GoodsQueue que) {
      super();
      this.que = que;
    }

    @Override
    public void run() {
      int i = 0;
      try {
        while (true) {
          String e = String.valueOf(i);
          que.put(e);
          i++;
          Thread.sleep(rad.nextInt(1000));// 生产者休息准备下一次生产
        }
      } catch (InterruptedException e1) {
      }
    }
  }

  static class Consumer extends Thread {
    GoodsQueue que;
    Random rad = new Random();

    public Consumer(GoodsQueue que) {
      super();
      this.que = que;
    }

    @Override
    public void run() {
      try {
        while (true) {
          que.take();
          Thread.sleep(rad.nextInt(1000));// 消费者休息准备下一次消费
        }
      } catch (InterruptedException e) {
      }
    }
  }

  public static void main(String[] args) throws InterruptedException {
    GoodsQueue que = new GoodsQueue(1);
    Producer pro = new Producer(que);
    Consumer con = new Consumer(que);
    con.start();
    Thread.sleep(500);
    pro.start();
  }
}

 

 

以上是关于线程的基本协作和生产者消费者的主要内容,如果未能解决你的问题,请参考以下文章

Java 线程中协作机制

11.9-全栈Java笔记: 线程并发协作(生产者/消费者模式)

线程协作-生产者/消费者问题

多线程之线程间协作的两种方式:waitnotifynotifyAll和Condition

JAVA SE基础篇60.线程协作

多线程_并发协作