實現一個容器,提供兩個方法 add, size
寫倆個線程,線程1添加10個元素道容器中,線程2實現監控元素得個數,當個數到5的時候,線程2給出提示並結束。
- 因爲涉及到兩個線程訪問同一個變量,所以應該是變量共享的,會使用到volatile或者synchronized,(只涉及到一個線程去修改資源,另外一個線程去訪問,所以這裏同步方法可能不大適合)
- 需要另外一個線程去輪循容器是否已經有5個元素來,這樣會很大的浪費資源
- wait和notify功能,兩個線程共同鎖定一個對象,通過這個對象,使用wait和notify 使倆個線程來啓東和釋放鎖,這裏需要搶到的一點 wait 會釋放鎖。
public class Container {
volatile List lists = new ArrayList();
public void add(Object o){
lists.add(o);
}
public int size(){
return lists.size();
}
public static void main(String[] args){
Container container = new Container();
final Object o = new Object();
new Thread(() ->{
synchronized (o){
System.out.println("t2 啓動");// t2 監控元素
if (container.size()!=5){
try {
o.wait();// 釋放
System.out.println("5個添加夠了 t1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 結束");
}
}, "t2").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()-> {
System.out.println("t1 啓動");// t1 添加元素
synchronized (o){
for (int i = 0; i < 10; i++) {
container.add(new Object());
System.out.println("add: " + i);
if (container.size() == 5) {
o.notify();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1 結束");
}
}, "t1").start();
}
}
執行上面的代碼會有個問題:t2 線程並沒有在t1線程增加到第5個元素得時候啓動,雖然在上面的代碼中t2線程有去notify t1線程,但是線程並沒有打印出來我們想要的結果,而是在t1結束之後纔打印出來。
之所以出現這個問題是因爲 notify 並不會去釋放鎖,所以即使在t1線程中notify t2線程,但是t2線程並沒有重新獲得鎖(在程序剛啓動的時候t2 進入 wait 狀態,釋放了鎖)。
解決的方法是,在t1程序 notify t2 得時候需要釋放鎖,使用的方法就是 讓t1也進入wait狀態,然後t2程序獲得鎖,執行結束之後,執行結束之後會釋放鎖,再notify t1線程運行。
notify之後,t1必須釋放鎖,t2退出後,也必須notify,通知t1繼續執行 * 整個通信過程比較繁瑣 * * 使用Latch(門閂)替代wait notify來進行通知 * 好處是通信方式簡單,同時也可以指定等待時間 * 使用await和countdown方法替代wait和notify * CountDownLatch不涉及鎖定,當count的值爲零時當前線程繼續運行 * 當不涉及同步,只是涉及線程通信的時候,用synchronized + wait/notify就顯得太重了 * 這時應該考慮countdownlatch/cyclicbarrier/semaphore
涉及到的方法:CountDownLatch(1),CountDown往下數,當1變爲0的時候門閂就開了,latch.countDown()調用一次數就往下-1;latch.await(),門閂的等待是不需要鎖定任何對象的;
public class Container {
volatile List lists = new ArrayList();
public void add(Object o){ lists.add(o); }
public int size(){ return lists.size(); }
public static void main(String[] args){
Container container = new Container();
final Object o = new Object();
CountDownLatch latch = new CountDownLatch(1);
new Thread(() ->{
System.out.println("t2 啓動");
if (container.size()!=5){
try {
latch.await();
// 可以指定等待時間
//latch.await(5000, TimeUnit.MILLISECONDS);
System.out.println("5個添加夠了 t1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 結束");
}, "t2").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()-> {
System.out.println("t1 啓動");
for (int i = 0; i < 10; i++) {
container.add(new Object());
System.out.println("add: " + i);
if (container.size() == 5) {
// 執行 -1 後count 爲0,打開門栓,t2 執行。
latch.countDown();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t1 結束");
}, "t1").start();
}
}
輸出:
t2 啓動
t1 啓動
add: 0
add: 1
add: 2
add: 3
add: 4
5個添加夠了 t1
t2 結束
add: 5
add: 6
add: 7
add: 8
add: 9
t1 結束