JAVA线程之生产者消费者问题
复习下JAVA线程基础知识:
1、线程的状态:
创建状态:创建了线程对象,此时线程有了相应的内存空间和其他资源,但处于不可运行状态。
就绪状态:线程对象调用start()方法启动线程,进入就绪状态,此时线程进入线程队列排队,此时已经具备运行的条件。
运行状态:线程抢占到cpu资源,此时线程进入运行状态,自动调用run()方法。
阻塞状态:正在运行的线程,让出cpu资源并暂时中止自己的执行,进入阻塞状态,在可执行状态可调用sleep()、suspend()、wait()等方法进入阻塞状态
,阻塞中不能进入线程排队抢占资源,只有阻塞消除后才能进入就绪状态。
死亡状态:线程调用stop()方法或run()执行结束后,即处于死亡状态
2、常用API
Thread.currentThread() 当前线程
Thread.isAlive 对象方法, 线程是否启动
Thread.join 对象方法,线程强制运行,其他线程必须等到该线程运行完毕后才能继续执行
Thread.sleep静态方法,当前线程休眠
Thread.interrup静态方法让,t中断正在执行的线程,会抛异常
Thread.setDaemon(true) 对象方法,设置此线程在后台运行
Thread线程的优先级,Thread.MIN_PRIORITY 1,Thread.NORM_PRIORITY 5,Thread.MAX_PRIORITY 10,mian线程 是5
Thread.yield()静态方法,线程的礼让,本线程释放资源,和其他线程一起再次公平竞争获取资源
3、实现Runnable接口相对于继承Thread类来说,有什么优势
1、适合多个相同程序代码去处理同一资源
2、可以避免由于Java单继承带来的局限
3、增强了程序的健壮性,代码能够被多个线程共享,代码与数据独立。
生产者消费者问题的引出:
生产者不断的往仓库中生产货物,货物装满时等消费者消费再生产,消费者不断的从仓库中消费货物,仓库为空时等生产者生产后再消费
参照网上的写法,自己写了一个
package com.lcx.test;import java.util.ArrayList; import java.util.List;/*** 生产者消费者问题* 生产者不断的往仓库中生产货物,货物装满时等消费者消费再生产,消费者不断的从仓库中消费货物,仓库为空时等生产者生产后再消费* 仓库类,存放货物的载体,仓库有最大容量。* 生产的货物数量+原有的数量 超过容量 就必须停止生产进入等待 让消费者消费消费后唤醒生产者。* 消费的货物数量 超过现有货物 就必须停止消费进入等待 让生产者生产货物后唤醒消费者。* 生产者类,负责生产* 消费者类,负责消费* wait() / notify() 使用这个实现* @author Administrator*/ public class Interview_2_ProductConsumer {public static void main(String[] args) {//生产者、消费者公用仓库资源Storehouse store = new Storehouse();Producter p1 = new Producter(10, store);Producter p2 = new Producter(20, store);Producter p3 = new Producter(30, store);Producter p4 = new Producter(50, store);Producter p5 = new Producter(50, store);Consumer c1 = new Consumer(20, store);Consumer c2 = new Consumer(10, store);Consumer c3 = new Consumer(50, store);Consumer c4 = new Consumer(60, store); // Consumer c5 = new Consumer(100, store);p1.start();p2.start();p3.start();p4.start();p5.start();c1.start();c2.start();c3.start();c4.start(); // c5.start();} } /*** 仓库类* @author Administrator**/ class Storehouse{//仓库存储最大数private static final int maxNum = 100;//货物载体private List<Object> goods = new ArrayList<Object>(); /*** 生产num个货物* @param num*/public void product(int num){synchronized (goods) { // System.out.print("开始生产,");/** 生产的数量超过仓库容量,一定要用while,因为不符合条件时进入线程等待(此时等待消费者消费),* 消费者消费后,唤醒生产者,生产者 还要继续判断是否可以生产,不能生产还得进入线程等待*/while (goods.size()+num>maxNum) {System.out.println("仓库现有:"+goods.size()+",生产:"+num+",超过最大容量"+maxNum+",无法进行生产");try {goods.wait();} catch (InterruptedException e) {e.printStackTrace();}}for(int i=0;i<num;i++){goods.add(new Object());}System.out.println("生产:"+num+",现有货物:"+goods.size());goods.notifyAll();}}/*** 消费num个货物* @param num*/public void consumer(int num){synchronized (goods) { // System.out.print("开始消费,");//消费的数量超过仓库容量while(num>goods.size()) {System.out.println("仓库现有:"+goods.size()+",消费:"+num+",超过现有量无法进行消费");try {goods.wait();} catch (InterruptedException e) {e.printStackTrace();}}for(int i=0;i<num;i++){goods.remove(goods.size()-1);}System.out.println("消费:"+num+",现有货物:"+goods.size());goods.notifyAll();}} } /*** 生产者* @author Administrator**/ class Producter extends Thread{//生产货物数量private int num;private Storehouse store;public Producter(int num, Storehouse store) {super();this.num = num;this.store = store;}@Overridepublic void run() {store.product(num);} } /*** 消费者* @author Administrator*/ class Consumer extends Thread{//消费货物数量private int num;private Storehouse store;public Consumer(int num, Storehouse store) {super();this.num = num;this.store = store;}@Overridepublic void run() {store.consumer(num);} } 结果截图
消费完了,还有剩余的货物,如果将消费者c5放开,结果如下
由于生产者都生产完了,还不够消费者消费,故 消费者进入等待状态,等待生产者生产后唤醒消费者消费,故会出现这种情况。
网上有JAVA 面试题一道,类似生产者消费者问题
编程实现:线程A向队列Q中不停写入数据,线程B从队列Q中不停读取数据(只要Q中有数据)。
代码实现如下:
package com.lcx.test;import java.util.ArrayList; import java.util.List;/***2.编程实现:线程A向队列Q中不停写入数据,线程B从队列Q中不停读取数据(只要Q中有数据)。*实质和生产者和消费者问题一样,* 情况一:如果要求先压入数据后马上弹出然后再才压入,就需要一个标志来确定是否读入或者写出了。* 此时,弹出和压入总是成对出现* 情况二:如果只要求,队列不为空就可弹出,队列没有满就可压入数字,就只需判断当前压入的数字有没有超过队列的最大长度 即可压入,当前队列不为空才弹出。* @author Administrator*/ public class Interview_2_ReadWrite {public static void main(String[] args) {Queue queue = new Queue(10);Pusher p1 = new Pusher(queue, 1);Pusher p2 = new Pusher(queue, 2);Pusher p3 = new Pusher(queue, 3);Pusher p4 = new Pusher(queue, 4);Poper po1 = new Poper(queue);Poper po2 = new Poper(queue);Poper po3 = new Poper(queue);Poper po4 = new Poper(queue);p1.start();p2.start();p3.start();p4.start();po1.start(); po2.start(); po3.start(); po4.start(); } } class Queue{//标志位,true 可弹出数字,false 可压入数字private boolean flag ;private final int maxLength;private List<Integer> queue = new ArrayList<Integer>() ;public Queue(int maxLength) {super();this.maxLength = maxLength;}/*** 往队列中压数字*/public void push(int num){//情况一synchronized (queue) {//压入的数字超过最大的下标while(flag){System.out.println("还未弹出数字,暂时不能压入数字");try {queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}queue.add(num);System.out.println("压入,数字下标index ="+ (queue.size()-1) + ",数字:" + queue.get(queue.size()-1));flag = true;queue.notifyAll();}//情况二/*synchronized (queue) {// 压入的数字超过最大的下标while (queue.size()+1 > maxLength) {System.out.println("队列已满,无法压入数字");try {queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}queue.add(num);System.out.println("压入,数字下标index ="+ (queue.size()-1) + ",数字:" + queue.get(queue.size()-1));queue.notifyAll();}*/}/*** 从队列中取数字*/public void pop(){//情况一synchronized (queue) {//取出的数字下标非法时while(!flag){System.out.println("还未压入数字,暂时不能弹出数字");try {queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("弹出,数字下标index ="+ (queue.size()-1) + ",数字:" + queue.get(queue.size()-1));queue.remove(queue.size()-1);flag = false;queue.notifyAll();}//情况二/*synchronized (queue) {//队列为空是while (queue.size() == 0 ) {System.out.println("队列已空,无法弹出数字");try {queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("弹出,数字下标index ="+ (queue.size()-1) + ",数字:" + queue.get(queue.size()-1));queue.remove(queue.size()-1);queue.notifyAll();}*/} } class Pusher extends Thread{private Queue queue;private int num;public Pusher(Queue queue,int num) {super();this.num = num;this.queue = queue;}@Overridepublic void run() {queue.push(num);}} class Poper extends Thread{private Queue queue;public Poper(Queue queue) {super();this.queue = queue;}@Overridepublic void run() {queue.pop();}}情况一结果如下:情况二截图:
总结
以上是生活随笔为你收集整理的JAVA线程之生产者消费者问题的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Scala-Unit6-final/ty
- 下一篇: 字符串:BF算法