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算法