你好我是辰兮,很高興你能來閱讀,本篇是整理了Java多線程中常見的生產者消費者問題,也是面試手寫代碼的高頻問題,分享獲取新知,大家共同進步!
1.JAVA基礎面試常考問題
: JAVA面試基礎常考題彙集2.JAVA面試SSM框架常考
:JAVA框架面試題彙集
一、生產者消費者問題
生產者消費者問題(英語:Producer-consumer problem),也稱有限緩衝問題(英語:Bounded-buffer problem),是一個多線程同步問題的經典案例。
該問題描述了兩個共享固定大小緩衝區的線程——即所謂的“生產者”和“消費者”——在實際運行時會發生的問題。
生產者的主要作用是生成一定量的數據放到緩衝區中,然後重複此過程。與此同時,消費者也在緩衝區消耗這些數據。該問題的關鍵就是要保證生產者不會在緩衝區滿時加入數據,消費者也不會在緩衝區中空時消耗數據。
生產消費者模型
生產者消費者模型具體來講,就是在一個系統中,存在生產者和消費者兩種角色,他們通過內存緩衝區進行通信,生產者生產消費者需要的資料,消費者把資料做成產品。生產消費者模式如下圖。
二、代碼實現
案例如下
首先定義一個類 ,這個類創建一個長度爲5的數組,然後用synchronized同步鎖實現生產數據和消費數據兩個方法,兩個方法中分別有 wait()和notify();
public class Resources {
private int[] arr = new int[5];
private int count = 0;
/**
* 生產一個數據
*/
synchronized public void product() throws Exception {
if(count == arr.length) {
wait();
}else {
int m = (int)Math.floor(Math.random()*10+1) ;
arr[count] = m;
count ++ ;
System.out.println("生產:"+m);
//喚醒消費者
notify();
}
}
/**
* 消費一個數據
*/
synchronized public void consume() throws Exception {
if(count == 0) {
wait();
}else {
int n = arr[count-1];
arr[count-1] = 0;
count--;
System.out.println("消費:"+n);
//喚醒生產者
notify();
}
}
}
然後創建生產者線程,引入剛剛的對象,用剛剛的對象調用生產的方法
public class ProduceThread extends Thread {
private Resources r;
public ProduceThread(Resources r) {
this.r = r;
}
public void run() {
try {
while(true) {
r.product();
Thread.sleep(1);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
然後創建消費者線程,同樣引入Resources對象,然後再用這個對象調用消費數據的方法
public class ConsumerThread extends Thread {
private Resources r;
public ConsumerThread(Resources r) {
this.r = r;
}
public void run() {
try {
while(true) {
r.consume();
Thread.sleep(1);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
測試類開始測試
public class Test {
public static void main(String[] args) {
Resources r = new Resources();
ProduceThread t1 = new ProduceThread(r);
ConsumerThread t2 = new ConsumerThread(r);
t1.start();
t2.start();
}
}
在main方法中,開啓消費者和生產者線程。因爲生產者和消費者用的是同一個對象的不同synchronized方法,所以兩個線程會產生producterAndConsumer 對象鎖的競爭
生產:4
消費:4
生產:7
消費:7
生產:8
生產:4
消費:4
消費:8
生產:4
消費:4
生產:2
消費:2
生產:8
生產:5
消費:5
消費:8
生產:3
生產:1
.....//無休止
三、拓展知識
1、爲什麼wait, notify 和 notifyAll這些方法不在thread類裏面?
①JAVA提供的鎖是對象級的而不是線程級的(如我例子中的list,list是對象),每個對象都有鎖,通過線程獲得。
②如果線程需要等待某些鎖那麼調用對象中的wait()方法就有意義了。如果wait()方法定義在Thread類中,線程正在等待的是哪個鎖就不明顯了。
③簡單的說,由於wait,notify和notifyAll都是鎖級別的操作,所以把他們定義在Object類中因爲鎖屬於對象。
補充:如何理解Java 中每個對象都有個鎖?
在Java語言中,每一個對象有一把鎖。線程可以使用synchronized關鍵字來獲取對象上的鎖。
java允許多線程併發控制,當多個線程同時操作一個可共享的資源變量時(如數據的增刪改查),將會導致數據不準確,相互之間產生衝突,因此加入同步鎖以避免在該線程沒有完成操作之前,被其他線程的調用,從而保證了該變量的唯一性和準確性。
2、java中notify 和 notifyAll有什麼區別?
notify()notifyAll()的共同點:
均能喚醒正在等待的線程,並且均是最後只有一個線程獲取資源對象的鎖。
notify()notifyAll()的不同點:
notify() 只能喚醒一個線程,而notifyall()能夠喚醒所有的線程,當線程被喚醒以後所有被喚醒的線程競爭獲取資源對象的鎖,其中只有一個能夠得到對象鎖,執行代碼。注意:wait()方法並不是在等待資源的鎖,而是在等待被喚醒。
3、未完待續.
The best investment is to invest in yourself
2020.06.13 記錄辰兮的第81篇博客