假設有這樣一種情況,有一個盤子,盤子裡隻能放一個雞蛋,A線程專門往盤子裡放雞蛋,如果盤子裡有雞蛋,則一直等到盤子裡沒雞蛋,B線程專門從盤子裡取雞蛋,如果盤子裡沒雞蛋,則一直等到盤子裡有雞蛋。這裡盤子是一個互斥區,每次放雞蛋是互斥的,每次取雞蛋也是互斥的,A線程放雞蛋,如果這時B線程要取雞蛋,由於A沒有釋放鎖,B線程處於等待狀態,進入阻塞隊列,放雞蛋之後,要通知B線程取雞蛋,B線程進入就緒隊列,反過來,B線程取雞蛋,如果A線程要放雞蛋,由於B線程沒有釋放鎖,A線程處於等待狀態,進入阻塞隊列,取雞蛋之後,要通知A線程放雞蛋,A線程進入就緒隊列。我們希望當盤子裡有雞蛋時,A線程阻塞,B線程就緒,盤子裡沒雞蛋時,A線程就緒,B線程阻塞,代碼如下:
[java]
import java.util.ArrayList;
import java.util.List;
/** 定義一個盤子類,可以放雞蛋和取雞蛋 */
public class Plate {
/** 裝雞蛋的盤子 */
List<Object> eggs = new ArrayList<Object>();
/** 取雞蛋 */
public synchronized Object getEgg() {
while (eggs.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Object egg = eggs.get(0);
eggs.clear();// 清空盤子
notify();// 喚醒阻塞隊列的某線程到就緒隊列
System.out.println("拿到雞蛋");
return egg;
}
/** 放雞蛋 */
public synchronized void putEgg(Object egg) {
while (eggs.size() > 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
eggs.add(egg);// 往盤子裡放雞蛋
notify();// 喚醒阻塞隊列的某線程到就緒隊列
System.out.println("放入雞蛋");
}
static class AddThread extends Thread {
private Plate plate;
private Object egg = new Object();
public AddThread(Plate plate) {
this.plate = plate;
}
public void run() {
plate.putEgg(egg);
}
}
static class GetThread extends Thread {
private Plate plate;
public GetThread(Plate plate) {
this.plate = plate;
}
public void run() {
plate.getEgg();
}
}
public static void main(String args[]) {
Plate plate = new Plate();
for(int i = 0; i < 10; i++) {
new Thread(new AddThread(plate)).start();
new Thread(new GetThread(plate)).start();
}
}
}
輸出結果:
[java]
放入雞蛋
拿到雞蛋
放入雞蛋
拿到雞蛋
放入雞蛋
拿到雞蛋
放入雞蛋
拿到雞蛋
放入雞蛋
拿到雞蛋
放入雞蛋
拿到雞蛋
放入雞蛋
拿到雞蛋
放入雞蛋
拿到雞蛋
放入雞蛋
拿到雞蛋
放入雞蛋
拿到雞蛋
8 l程序開始,A線程判斷盤子是否為空,放入一個雞蛋,並且喚醒在阻塞隊列的一個線程,阻塞隊列為空;假設CPU又調度瞭一個A線程,盤子非空,執行等待,這個A線程進入阻塞隊列;然後一個B線程執行,盤子非空,取走雞蛋,並喚醒阻塞隊列的A線程,A線程進入就緒隊列,此時就緒隊列就一個A線程,馬上執行,放入雞蛋;如果再來A線程重復第一步,在來B線程重復第二步,整個過程就是生產者(A線程)生產雞蛋,消費者(B線程)消費雞蛋。
前段時間看瞭張孝祥老師線程的視頻,講述瞭一個其學員的面試題,也是線程通信的,在此也分享一下。
題目:子線程循環10次,主線程循環100次,如此循環100次,好像是空中網的筆試題。
[java]
public class ThreadTest2 {
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() {
@Override
public void run() {
threadExecute(business, "sub");
}
}).start();
threadExecute(business, "main");
}
public static void threadExecute(Business business, String threadType) {
for(int i = 0; i < 100; i++) {
try {
if("main".equals(threadType)) {
business.main(i);
} else {
business.sub(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Business {
private boolean bool = true;
public synchronized void main(int loop) throws InterruptedException {
while(bool) {
this.wait();
}
for(int i = 0; i < 100; i++) {
System.out.println("main thread seq of " + i + ", loop of " + loop);
}
bool = true;
this.notify();
}
public synchronized void sub(int loop) throws InterruptedException {
while(!bool) {
this.wait();
}
for(int i = 0; i < 10; i++) {
System.out.println("sub thread seq of " + i + ", loop of " + loop);
}
bool = false;
this.notify();
}
}
大傢註意到沒有,在調用wait方法時,都是用while判斷條件的,而不是if,在wait方法說明中,也推薦使用while,因為在某些特定的情況下,線程有可能被假喚醒,使用while會循環檢測更穩妥。