Java多线程-生产者和消费者

标签(空格分隔): java thread


简介:
随着操作系统的不断更新迭代,多线程编程已经变的十分常见,java虚拟机的多线程一般也是建立在操作系统本地native线程之上的,从而不必自己管理线程间的切换,直接交由本机操作系统进行调度和管理。对于java程序来说,它是运行在虚拟机上的,由于线程间很多资源都是共享的,比如全局数据等等,因此,线程间的同步也就不是那么复杂的。java也提供了许多实现线程同步的方法,我们这里主要分析wait和notify。

问题:
在一个盒子里,能放置一定数量的物体A,同一时间只能准许一个人往箱子里放物体或者一个人从盒子中取出物品。当盒子满了,再往里面放物体的人需要等待。当盒子空了,从盒子中取出物品的人需要等待。
这就是一个典型的生产者和消费者模型,下面在代码的基础上模拟这个过程。
1. 盒子对象

public class DataManager {

  private static final int LIMIT = 20;
  private List<String> datas = new ArrayList<>();

  public synchronized void put( String data ) {
    while ( datas.size() == LIMIT ) {
      // size limit, need wait.
      try {
        // 处于wait等待的线程会暂时释放锁,当其被唤醒时,会重新获得锁
        wait();
      } catch ( InterruptedException e ) {
        e.printStackTrace();
      }
      // 线程被唤醒后,重新检查条件
    }
    datas.add( data );
    // 唤醒所有等待的线程
    notifyAll();
  }

  public synchronized String get() {

    while ( datas.size() == 0 ) {
      // datas is empty, need wait.
      try {
        wait();
      } catch ( InterruptedException e ) {
        e.printStackTrace();
      }
    }
    String data = datas.get( 0 );
    datas.remove( 0 );
    notifyAll();
    return data;
  }

}

DataManger展示了盒子这个模型,这里需要理解wait和notify以及notifyAll这几个方法。

2.生产者模型

public class Producer implements Runnable {

  static int count = 0;
  private DataManager mDataManager;

  public Producer( DataManager dataManager ) {
    mDataManager = dataManager;
  }

  @Override
  public void run() {
    while ( true ) {
      try {
        sleep( 100 );
      } catch ( InterruptedException e ) {
        e.printStackTrace();
      }
      String data = "data - " + count++;
      mDataManager.put( data );
      System.out.println( "Producer " + Thread.currentThread().getName() + ", put -> " + data );
    }
  }
}

3.消费者模型

public class Consumer implements Runnable {

  private DataManager mDataManager;

  public Consumer( DataManager dataManager ) {
    mDataManager = dataManager;
  }

  @Override
  public void run() {
    while ( true ) {
      try {
        sleep( 100 );
      } catch ( InterruptedException e ) {
        e.printStackTrace();
      }
      String data = mDataManager.get();
      System.out.println( "Consumer " + Thread.currentThread().getName() + ", get -> " + data );
    }
  }
}

Client端代码

public class Client {

  public static void main( String[] args ) {
    DataManager dataManager = new DataManager();
    for ( int i = 0; i < 20; i++ ) {
      new Thread( new Producer( dataManager ) ).start();
      new Thread( new Consumer( dataManager ) ).start();
    }
  }

}

总结:
整体结构来说是比较简单的,所有生产者Producer和所有消费者Consumer共享同一个盒子DataManager。同时对盒子进行存取操作。对于上述代码,应该重点理解wait、notify以及notifyAll它们作用的对象,它们三者必须在同步块中使用,同时理解wait会释放对象锁,这点和sleep有区别,而被唤醒后又会重新持有对象锁,这个是线程同步的关键,也是理解这些代码的关键。当然java还有许多api提供了线程同步的方法,比如锁机制,其实两者在原理上是大同小异,都是基于本机操作系统的线程同步机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值