1. 程式人生 > >Java併發包的門栓

Java併發包的門栓

package ThreadLearn;
/**
 * 曾經的面試題:(淘寶?)
 * 實現一個容器,提供兩個方法,add,size
 * 寫兩個執行緒,執行緒1新增10個元素到容器中,執行緒2實現監控元素的個數,當個數到5個時,執行緒2給出提示並結束
 * 
 * 給lists新增volatile之後,t2能夠接到通知,但是,t2執行緒的死迴圈很浪費cpu,如果不用死迴圈,該怎麼做呢?
 * 
 * 這裡使用wait和notify做到,wait會釋放鎖,而notify不會釋放鎖
 * 需要注意的是,運用這種方法,必須要保證t2先執行,也就是首先讓t2監聽才可以
 * 
 * 閱讀下面的程式,並分析輸出結果
 * 可以讀到輸出結果並不是size=5時t2退出,而是t1結束時t2才接收到通知而退出
 * 想想這是為什麼?
 * 
 * notify之後,t1必須釋放鎖,t2退出後,也必須notify,通知t1繼續執行
 * 整個通訊過程比較繁瑣
 * 
 * 使用Latch(門閂)替代wait notify來進行通知
 * 好處是通訊方式簡單,同時也可以指定等待時間
 * 使用await和countdown方法替代wait和notify
 * CountDownLatch不涉及鎖定,當count的值為零時當前執行緒繼續執行
 * 當不涉及同步,只是涉及執行緒通訊的時候,用synchronized + wait/notify就顯得太重了
 * 這時應該考慮countdownlatch/cyclicbarrier/semaphore
 * 
 */

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class T019_05 {
	// 新增volatile,使t2能夠得到通知
		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) {
			T019_05 c = new T019_05();

			CountDownLatch latch = new CountDownLatch(1);

			new Thread(() -> {
				System.out.println("t2啟動");
				if (c.size() != 5) {
					try {
						latch.await();
						
						//也可以指定等待時間
						//latch.await(5000, TimeUnit.MILLISECONDS);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println("t2 結束");

			}, "t2").start();

			try {
				TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}

			new Thread(() -> {
				System.out.println("t1啟動");
				for (int i = 0; i < 10; i++) {
					c.add(new Object());
					System.out.println("add " + i);

					if (c.size() == 5) {
						// 開啟門閂,讓t2得以執行
						latch.countDown();
					}

					try {
						TimeUnit.SECONDS.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

			}, "t1").start();

		}
}