java併發程式設計 一道經典多執行緒題的2種解法

2021-06-08 10:54:35 字數 3021 閱讀 6771

問題的描述

啟動3個執行緒列印遞增的數字, 執行緒1先列印1,2,3,4,5, 然後是執行緒2列印6,7,8,9,10, 然後是執行緒3列印11,12,13,14,15. 接著再由執行緒1列印16,17,18,19,20....以此類推, 直到列印到75. 程式的輸出結果應該為:

執行緒1: 1

執行緒1: 2

執行緒1: 3

執行緒1: 4

執行緒1: 5

執行緒2: 6

執行緒2: 7

執行緒2: 8

執行緒2: 9

執行緒2: 10

...

執行緒3: 71

執行緒3: 72

執行緒3: 73

執行緒3: 74

執行緒3: 75

解法一: 採用原始的synchronized, wait(), notify(), notifyall()等方式控制線程.

public class numberprintdemo catch (interruptedexception e)

// 當state=1時, 輪到執行緒1列印5次數字

for (int j = 0; j < 5; j++)

system.out.println();

// 執行緒1列印完成後, 將state賦值為2, 表示接下來將輪到執行緒2列印

state = 2;

// notifyall()方法喚醒在pn上wait的執行緒2和執行緒3, 同時執行緒1將退出同步**塊, 釋放pn鎖.

// 因此3個執行緒將再次競爭pn鎖

// 假如執行緒1或執行緒3競爭到資源, 由於state不為1或3, 執行緒1或執行緒3將很快再次wait, 釋放出剛到手的pn鎖.

// 只有執行緒2可以通過state判定, 所以執行緒2一定是執行下次列印任務的執行緒.

// 對於執行緒2來說, 獲得鎖的道路也許是曲折的, 但前途一定是光明的.

pn.notifyall();}}

}}, "執行緒1").start();

new thread(new runnable() catch (interruptedexception e)

for (int j = 0; j < 5; j++)

system.out.println();

state = 3;

pn.notifyall();}}

}}, "執行緒2").start();

new thread(new runnable() catch (interruptedexception e)

for (int j = 0; j < 5; j++)

system.out.println();

state = 1;

pn.notifyall();}}

}}, "執行緒3").start();}}

解法二: 採用jdk1.5並發包提供的lock, condition等類的相關方法控制線程.

public class numberprint implements runnable

lock.lock();

while (state != 1)

try catch (interruptedexception e)

// 如果執行緒1競爭到了lock, 也通過了state判定, 將執行列印任務

for (int j = 0; j < 5; j++)

system.out.println();

// 列印完成後將state賦值為2, 表示下一次的列印任務將由執行緒2執行

state = 2;

// 喚醒在c2分支上wait的執行緒2

c2.signal();

} finally }}

}, "執行緒1").start();

new thread(new runnable() catch (interruptedexception e)

for (int j = 0; j < 5; j++)

system.out.println();

state = 3;

c3.signal();

} finally }}

}, "執行緒2").start();

new thread(new runnable() catch (interruptedexception e)

for (int j = 0; j < 5; j++)

system.out.println();

state = 1;

c1.signal();

} finally }}

}, "執行緒3").start();

}public static void main(string args) }

總結: 對比解法一和解法二, 顯然解法二是更好的解決方案. 解法一的問題在於無法進行精確喚醒, 比如執行緒1執行完列印任務並呼叫pn.notifyall()方法後, 3個執行緒將再次競爭鎖, 而不是精確喚醒執行緒2. 雖然執行緒2最終將贏得鎖, 下一次的列印任務也肯定會由執行緒2執行, 但是競爭的持續時間是不可預知的, 只能看執行緒2的人品.

最糟糕的情形可以是: 執行緒3競爭到了鎖, 緊接著wait. 接下來執行緒1也競爭到了鎖, 然後執行緒1也wait. 此時就再也沒有其他執行緒跟執行緒2競爭了, 執行緒2終於艱難的贏得了鎖...

留下3個問題供有興趣的朋友思考:

1. 解法一和解法二中的

while (state != xx)是否可以換成if(state != xx), 為什麼?

2. 解法一的中的

pn.notifyall()是否可以換成

pn.notify(), 為什麼?

3. 是否可以用wait(), notify(), notifyall()等方法完成類似解法二的精確喚醒, 請給出方案或**.--這個問題我思考了很久, 卻沒有頭緒. 關鍵的困難在於必須呼叫pn的wait()方法和notifyall()方法, 而不能是其他物件的wait()和notifyall()方法.

一道經典plsql程式設計練習題

最近在學plsql,苦於找不到練習題,所以自己寫了乙個用於練習.要求如下 查詢scott使用者的 emp表中的所有資料,如果 job為 工資大於 2500 則下調百分之 10 如果小於 則增加百分之 10,如果 job為 工資小於 則上調百分之 10 最後符合條件的人 插入到 test 表中.並且列...

js的一道經典題目

今天碰到一道題,裡面既包含了匿名函式的知識,也包含了預編譯,函式的傳參 形參 感覺迷迷糊糊的,所以想著做個總結。var foo function foo console.log foo.n foo console.log foo.n 上面的 可以寫成這樣,看解釋 1 var foo 2 functi...

js的一道經典題目

今天碰到一道題,裡面既包含了匿名函式的知識,也包含了預編譯,函式的傳參 形參 感覺迷迷糊糊的,所以想著做個總結。var foo function foo console.log foo.n foo console.log foo.n 上面的 可以寫成這樣,看解釋 1 var foo 2 functi...