Java多執行緒——簡單的生產者消費者問題練習
阿新 • • 發佈:2021-01-29
Java多執行緒生產者消費者
Java的多執行緒學習中,因為有練習的要求,扔個生產者消費者的程式碼放上來:
先解釋一下,裡面有三個類,分別是cook(廚師),waiter(服務生)和food,廚師生產食物,而服務生則端走食物。那麼先寫一個用鎖去同步的多執行緒,程式碼其實很簡單,就是廚師對食物進行更改,直接修改食物的型別(手撕雞或者滷豬腳),就算是食物生產完成了。
public class TestThread {
public static void main(String[] args){
food f = new food ();
cook c = new cook(f);
waiter w = new waiter(f);
Thread t1 = new Thread(c);
Thread t2 = new Thread(w);
t1.start();
t2.start();
}
}
class cook implements Runnable{
food f;
static final int count = 5;
cook(food f){
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < count; i++) {
f.l.lock();
if(i % 2 == 0) {
f.setName("手撕雞");
f.setTaste("麻辣味");
f.setFood();
}else{
f.setName ("滷豬腳");
f.setTaste("滷煮味");
f.setFood();
}
f.l.unlock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class waiter implements Runnable{
food f;
static final int count = 5;
waiter(food f){
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < count; i++) {
f.l.lock();
f.getFood();
f.l.unlock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class food{
String name;
String taste;
Lock l;
public food() {
l = new ReentrantLock();
}
public void setFood(){
System.out.println("廚師製作了" + taste + name);
}
public void getFood(){
System.out.println("服務員端走了" + taste + name);
}
public food(String name, String taste) {
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
l = new ReentrantLock();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTaste() {
return taste;
}
public void setTaste(String taste) {
this.taste = taste;
}
}
那麼這段程式碼執行的一個結果是這樣的:
可以看到,我們雖然設定了執行緒同步(用的是顯式鎖),但是沒規定是廚師先上菜還是服務員先端盤子,於是乎就出現了上圖的服務員先端走了未初始化的food的情況。
鑑於課程的內容,這邊就直接在food類中加一個訊號量,用類似互斥鎖的同步方法(其實生產者消費者問題,應該是廚師製作食物達到上限後,才沉睡,但是本問題是將一個食物反覆操作,相當於上限只能是1),(廚師做完活以後就睡眠並喚醒服務員,服務員做完後也沉睡並喚醒廚師,原本是準備照著這麼做的,結果我用的不是繼承thread,而是繼承Runnable介面,所以似乎不能這麼用),那麼用一個boolean訊號量來做這個同步就好,程式碼和結果是這樣的:
class cook implements Runnable{
food f;
static final int count = 50;
cook(food f){
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < count; i++) {
if(!f.flag) {
f.l.lock();
if (i % 2 == 0) {
f.setName("手撕雞");
f.setTaste("麻辣味");
f.setFood();
} else {
f.setName("滷豬腳");
f.setTaste("滷煮味");
f.setFood();
}
f.flag = true;
f.l.unlock();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class waiter implements Runnable{
food f;
static final int count = 50;
waiter(food f){
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < count; i++) {
if(f.flag) {
f.l.lock();
f.getFood();
f.flag = false;
f.l.unlock();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
其中,在food類里加入了一個flag,這裡覺得太長就不全放上來了,其實程式碼的修改不多。
這個時候執行的結果就比較舒適了:
這就是一個簡單的生產者消費者執行緒同步練習。