生產(chǎn)者有生產(chǎn)任務(wù)难述,消費者有消費任務(wù)叫胖,生產(chǎn)和消費可以同時進行,生產(chǎn)和消費的都是同一產(chǎn)品瞎领。
1锌畸、搭建示例
實現(xiàn)多線程同時生產(chǎn)并消費產(chǎn)品
1.創(chuàng)建一個類用于存放產(chǎn)品的信息勇劣;
2.類中包括生產(chǎn)產(chǎn)品和消費產(chǎn)品的功能;
3.創(chuàng)建一個類用于存放生產(chǎn)任務(wù)蹋绽;
4.創(chuàng)建一個類用于存放消費任務(wù)芭毙;
//描述產(chǎn)品
class Product {
private String name;
private int count;
private boolean flag;
// 生產(chǎn)產(chǎn)品的功能
public synchronized void produce(String name) {
if (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = count + "個" + name;
System.out.println(Thread.currentThread().getName() + "生產(chǎn)了" + this.name);
count++;
flag = true;
notify();
}
// 消費產(chǎn)品的功能
public synchronized void consume() {
if (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "消費了" + this.name);
flag = false;
notify();
}
}
// 生產(chǎn)任務(wù)
class Producer implements Runnable {
private Product pro;
public Producer(Product pro) {
this.pro = pro;
}
public void run() {
while (true) {
pro.produce("筆記本");
}
}
}
// 消費任務(wù)
class Consumer implements Runnable {
private Product pro;
public Consumer(Product pro) {
this.pro = pro;
}
public void run() {
while (true) {
pro.consume();
}
}
}
public class Demo1 {
public static void main(String[] args) {
Product pro = new Product();
Producer producer = new Producer(pro);
Consumer consumer = new Consumer(pro);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
t1.start();
t2.start();
}
}
此時結(jié)果如下:
當只有一個生產(chǎn)線程以及只有一個消費線程時,線程安全卸耘,實現(xiàn)了預(yù)期的效果,但是當有多個生產(chǎn)線程或消費線程時粘咖,結(jié)果如何蚣抗?
2、生產(chǎn)與消費的安全問題
為了實現(xiàn)更快的生產(chǎn)與消費瓮下,將生產(chǎn)線程與消費線程分別增加一個翰铡,代碼如下:
public class Demo1 {
public static void main(String[] args) {
Product pro = new Product();
Producer producer = new Producer(pro);
Consumer consumer = new Consumer(pro);
Thread t0 = new Thread(producer);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
Thread t3 = new Thread(consumer);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
此時結(jié)果如下:
這就出現(xiàn)了安全問題,例如連續(xù)生產(chǎn)多個產(chǎn)品但未消費以及連續(xù)消費同一個產(chǎn)品的情況讽坏。出現(xiàn)錯誤的原因是線程被喚醒之后沒有判斷flag標記而直接向下執(zhí)行任務(wù)代碼锭魔,以這段動畫為例:
為了便于閱讀文字,動畫切換間隔設(shè)置的比較長路呜。
因為T1線程被喚醒前迷捧,flag標記已經(jīng)被改為了true,假如T1線程被喚醒后能夠判斷flag的話胀葱,錯誤就可以避免漠秋,修改代碼如下:
// 生產(chǎn)產(chǎn)品的功能
public synchronized void produce(String name) {
// 將if改為while
while (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = count + "個" + name;
System.out.println(Thread.currentThread().getName() + "生產(chǎn)了" + this.name);
count++;
flag = true;
notify();
}
// 消費產(chǎn)品的功能
public synchronized void consume() {
// 將if改為while
while (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "消費了" + this.name);
flag = false;
notify();
}
此時結(jié)果如下:
雖然此時可以讓線程返回去判斷flag標記,看似解決了問題抵屿,但實際運行時卻出現(xiàn)了線程死鎖庆锦。接續(xù)接著之前的動畫,分析出現(xiàn)死鎖的原因:
此時假設(shè)T0線程喚醒的不是T1線程轧葛,而是T2或T3線程搂抒,那么T2或T3線程判斷flag標記后可正常執(zhí)行消費產(chǎn)品艇搀,因此實際上要保證,生產(chǎn)線程要喚醒消費線程求晶,消費線程要喚醒生產(chǎn)線程中符,然而notify()方法喚醒的是線程池中的任意線程,將notify()改為notifyAll()方法誉帅,雖然會犧牲程序的性能淀散,但線程全部被喚醒,就不會出現(xiàn)死鎖的現(xiàn)象了蚜锨,改正所有錯誤的代碼如下:
//描述產(chǎn)品
class Product {
private String name;
private int count;
private boolean flag;
// 生產(chǎn)產(chǎn)品的功能
public synchronized void produce(String name) {
while (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = count + "個" + name;
System.out.println(Thread.currentThread().getName() + "生產(chǎn)了" + this.name);
count++;
flag = true;
notifyAll();
}
// 消費產(chǎn)品的功能
public synchronized void consume() {
while (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "消費了" + this.name);
flag = false;
notifyAll();
}
}
// 生產(chǎn)任務(wù)
class Producer implements Runnable {
private Product pro;
public Producer(Product pro) {
this.pro = pro;
}
public void run() {
while (true) {
pro.produce("筆記本");
}
}
}
// 消費任務(wù)
class Consumer implements Runnable {
private Product pro;
public Consumer(Product pro) {
this.pro = pro;
}
public void run() {
while (true) {
pro.consume();
}
}
}
public class Demo1 {
public static void main(String[] args) {
Product pro = new Product();
Producer producer = new Producer(pro);
Consumer consumer = new Consumer(pro);
Thread t0 = new Thread(producer);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
Thread t3 = new Thread(consumer);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
注意:
隨著JKD版本的更新档插,在1.5版本之后出現(xiàn)比synchronized更加強大的實現(xiàn)同步鎖的方法,詳情參考使用Lock接口與Condition接口實現(xiàn)生產(chǎn)者與消費者亚再。
版權(quán)聲明:歡迎轉(zhuǎn)載郭膛,歡迎擴散,但轉(zhuǎn)載時請標明作者以及原文出處氛悬,謝謝合作则剃! ↓↓↓