執行緒練習:生產者-消費者問題
阿新 • • 發佈:2021-01-31
生產者-消費者問題
問題描述
生產者(Productor)將產品交給店員(Clerk),而消費者(Consumer)從店員處取走產品,店員一次只能持有固定數量的產品(比如:20),如果生產者試圖生產更多的產品,店員會叫生產者停一下,如果店中有空位放產品了再通知生產者繼續生產;如果店中沒有產品了,店員會告訴消費者等一下,如果店中有產品了再通知消費者來取走產品。
wait()/notify()/notifyall()方法
package com.java.ht;
public class Demo1 {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer p = new Producer(clerk);
Consumer c = new Consumer(clerk);
new Thread(p, "生產者A").start();
new Thread(p, "生產者B").start();
new Thread(c, "消費者A" ).start();
new Thread(c, "消費者B").start();
}
}
class Clerk{
private int product = 0;
public synchronized void getProduct(){
while(product >= 20){
System.out.println("貨架已滿");
try {
//如果貨架滿了,則需要讓該執行緒等待,生產者停止生產
wait ();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":" + ++product);
//賣出貨物後,需要喚醒全部執行緒,其他消費者可以繼續消費,生產者繼續生產
notifyAll();
}
public synchronized void sellProduct(){
while (product <= 0){
System.out.println("貨架上缺貨");
try {
//如果沒有貨物了,消費者需要等待
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":" + --product);
//消費完需要喚醒其他執行緒,生產需要生產,其他消費者消費
notifyAll();
}
}
class Producer implements Runnable{
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.getProduct();
}
}
}
class Consumer implements Runnable{
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sellProduct();
}
}
}
- 方法最後一定要記得喚醒執行緒
- wait()一定要放在迴圈體裡面,不然可能會出現虛假喚醒
Lcok和Condition
package com.java.ht;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo2 {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer p = new Producer(clerk);
Consumer c = new Consumer(clerk);
new Thread(p, "生產者A").start();
new Thread(p, "生產者B").start();
new Thread(c, "消費者A").start();
new Thread(c, "消費者B").start();
}
}
class Clerk{
private int product = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void getProduct(){
lock.lock();
try {
if(product >= 20){
//貨架已滿,停止生產
System.out.println("貨架已滿");
condition.await();
} else {
System.out.println(Thread.currentThread().getName() + ":" + ++product);
//生產了貨物,喚醒其他執行緒
condition.signalAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void sellProduct(){
lock.lock();
try {
if(product <= 0){
//貨架上沒有貨物,需要讓消費者等待
condition.await();
} else {
System.out.println(Thread.currentThread().getName() + ":" + --product);
//消費者消費完成,喚醒其他執行緒,消費者可以繼續消費,生產者可以繼續生產
condition.signalAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
class Producer implements Runnable{
private Clerk clerk;
public Producer1(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.getProduct();
}
}
}
class Consumer implements Runnable{
private Clerk clerk;
public Consumer1(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sellProduct();
}
}
}
- 記得最後使用unlock()解鎖
- Condition是Lock裡面的newCondition()產生的
- await(),signal(),signalAll()方法和Object類中的wait(),notify(),notifyAll()一樣
- 與上一種方法比,這種的效率更高