JDK5新特性之線程同步工具類(三)
來源:程序員人生 發布時間:2014-11-22 08:46:08 閱讀次數:3321次
1. Semaphore
Semaphore可以控制同時訪問資源的線程個數, 例如: 實現1個文件允許的并發訪問數.
Semaphore實現的功能就類似廁所有5個坑, 加入有10個人要上廁所, 那末同時只能有5個人能夠占用, 當5個人中的任何1個人離開后, 其中在等待的另外5個人中就有1個可以占用了. 另外等待的5個人中可以是隨機取得優先機會,
也能夠使依照先來后到的順序取得機會, 這取決于構造Semaphore對象時傳入的參數選項.
public class SemaphoreTest {
public static void main(String[] args) {
// 創建1個線程池
ExecutorService service = Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3); // 表示當前有3盞燈(允許3個并發)
// 啟動5個線程
for (int i = 0; i < 5; i++) {
service.execute(new Runnable() {
public void run() {
try {
sp.acquire(); // 點亮1盞燈
// availablePermits: 表示可使用的燈
System.out.println("線程" + Thread.currentThread().getName()
+ " 進入,當前已有" + (3 - sp.availablePermits()) + "個并發");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("線程" + Thread.currentThread().getName() + " 行將離開");
sp.release(); // 熄滅1盞燈(釋放)
System.out.println("線程" + Thread.currentThread().getName()
+ " 已離開,當前已有" + (3 - sp.availablePermits()) + "個并發");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
單個Semaphore對象可以實現互斥鎖的功能, 并且可以是由1個線程取得了"鎖", 再由另外一個線程釋放"鎖", 這可利用于死鎖恢復的1些場合.
線程pool⑴-thread⑴ 進入,當前已有1個并發
線程pool⑴-thread⑵ 進入,當前已有2個并發
線程pool⑴-thread⑷ 進入,當前已有3個并發
線程pool⑴-thread⑷ 行將離開
線程pool⑴-thread⑷ 已離開,當前已有2個并發
線程pool⑴-thread⑶ 進入,當前已有3個并發
線程pool⑴-thread⑵ 行將離開
線程pool⑴-thread⑵ 已離開,當前已有2個并發
線程pool⑴-thread⑸ 進入,當前已有3個并發
線程pool⑴-thread⑸ 行將離開
線程pool⑴-thread⑸ 已離開,當前已有2個并發
線程pool⑴-thread⑴ 行將離開
線程pool⑴-thread⑴ 已離開,當前已有1個并發
線程pool⑴-thread⑶ 行將離開
線程pool⑴-thread⑶ 已離開,當前已有0個并發
2. CyclicBarrier
表示大家彼此等待,集合好后才開始動身,分散活動后又在指定地點集合碰面。
這就好比全部公司的人員里利用周末時間集體遠足1樣,先各自從家動身到公司集合后,再同時動身到公園游玩,在指定地點集合后再同時開始就餐。
public class CyclicBarrierTest {
public static void main(String[] args) {
// 開啟1個線程池
ExecutorService executorService = Executors.newCachedThreadPool();
// 參數3: 表示有3個到齊了才可以往下走,否則1直處于等待狀態
final CyclicBarrier cb = new CyclicBarrier(3);
// 創建3個線程
for(int i = 0; i < 3; i++){
executorService.execute(new Runnable(){
@Override
public void run(){
try {
Thread.sleep((long)(Math.random() * 1000)); // 每一個線程“休息”的時間不同
System.out.println("線程" + Thread.currentThread().getName()
+ "行將到達集合地點1,當前已有" + (cb.getNumberWaiting() + 1)
+ "個已到達," + (cb.getNumberWaiting() == 2 ? "都到齊了,繼續前進" : "正在等候"));
cb.await(); // 先到的等待后到的,當3個都到達時才會繼續向下履行
Thread.sleep((long)(Math.random() * 1000)); // 每一個線程“休息”的時間不同
System.out.println("線程" + Thread.currentThread().getName()
+ "行將到達集合地點2,當前已有" + (cb.getNumberWaiting() + 1)
+ "個已到達," + (cb.getNumberWaiting() == 2 ? "都到齊了,繼續前進" : "正在等候"));
cb.await();
Thread.sleep((long)(Math.random()*1000)); // 每一個線程“休息”的時間不同
System.out.println("線程" + Thread.currentThread().getName()
+ "行將到達集合地點3,當前已有" + (cb.getNumberWaiting() + 1)
+ "個已到達,"+ (cb.getNumberWaiting() == 2 ? "都到齊了,繼續前進" : "正在等候"));
cb.await();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
executorService.shutdown();
}
}
3個線程干完各自的任務,在不同的時刻到達集合地點后,就能夠接著忙各自的工作去了,再到達新的集合點,再去忙各自的工作。
線程pool⑴-thread⑵行將到達集合地點1,當前已有1個已到達,正在等候
線程pool⑴-thread⑶行將到達集合地點1,當前已有2個已到達,正在等候
線程pool⑴-thread⑴行將到達集合地點1,當前已有3個已到達,都到齊了,繼續前進
線程pool⑴-thread⑵行將到達集合地點2,當前已有1個已到達,正在等候
線程pool⑴-thread⑶行將到達集合地點2,當前已有2個已到達,正在等候
線程pool⑴-thread⑴行將到達集合地點2,當前已有3個已到達,都到齊了,繼續前進
線程pool⑴-thread⑶行將到達集合地點3,當前已有1個已到達,正在等候
線程pool⑴-thread⑵行將到達集合地點3,當前已有2個已到達,正在等候
線程pool⑴-thread⑴行將到達集合地點3,當前已有3個已到達,都到齊了,繼續前進
3. CountDownLatch
猶如倒計時計數器, 調用CountDownLatch對象的countDown方法就將計數器減1, 當計數到達0時, 則所有等待者或單個等待者開始履行.
利用: 裁判1聲口令, 運動員同時開始奔跑, 當所有運動員都跑到終點后裁判公布結果. 還可以實現1個計劃需要多個領導都簽字后才能繼續向下實行的情況.
public class CountDownLatchTest {
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newCachedThreadPool();
// 子計數器, count為1
final CountDownLatch subCounter = new CountDownLatch(1);
// 主計數器, count為3
final CountDownLatch mainCounter = new CountDownLatch(3);
for(int i = 0; i < 3; i++){
service.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println("線程 "+ Thread.currentThread().getName()+"正準備接受命令!");
subCounter.await(); // 子線程等待
System.out.println("線程 "+ Thread.currentThread().getName()+"已接受命令!");
Thread.sleep((long)Math.random() * 10000);
System.out.println("線程 "+ Thread.currentThread().getName()+"回應命令處理結果!");
mainCounter.countDown(); // 將計數器身上的計數減1, 當計數為0時, 主線程將開始履行
} catch (Exception e) {
e.printStackTrace();
}
}
} );
}
Thread.sleep((long)Math.random() * 1000);
System.out.println("線程 "+ Thread.currentThread().getName()+"行將發布命令!");
subCounter.countDown(); // 將計數器身上的計數減1, 當計數為0時, 子線程開始履行
System.out.println("線程 "+ Thread.currentThread().getName()+"已發送命令,正在等待結果!");
mainCounter.await(); // 主線程等待
System.out.println("線程 "+ Thread.currentThread().getName()+"已收到所有響應結果!");
service.shutdown();
}
}
線程 pool⑴-thread⑴正準備接受命令!
線程 pool⑴-thread⑶正準備接受命令!
線程 pool⑴-thread⑵正準備接受命令!
線程 main行將發布命令!
線程 main已發送命令,正在等待結果!
線程 pool⑴-thread⑵已接受命令!
線程 pool⑴-thread⑶已接受命令!
線程 pool⑴-thread⑴已接受命令!
線程 pool⑴-thread⑶回應命令處理結果!
線程 pool⑴-thread⑵回應命令處理結果!
線程 pool⑴-thread⑴回應命令處理結果!
線程 main已收到所有響應結果!
4. Exchanger
用于實現兩個人之間的數據交換, 每一個人在完成1定的事務后想與對方交換數據, 第1個先拿出數據的人將1直等待第2個人拿著數據到來時, 才能彼此交換數據:
public class ExchangerTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
service.execute(new Runnable() {
@Override
public void run() {
try {
String data1 = "aaa";
System.out.println("線程 " + Thread.currentThread().getName() + " 正在把數據: " + data1 + " 換出去!");
Thread.sleep((long) Math.random() * 10000);
String data2 = (String) exchanger.exchange(data1);
System.out.println("線程 " + Thread.currentThread().getName() + " 換回的數據為:" + data2);
} catch (Exception e) {
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
try {
String data1 = "bbb";
System.out.println("線程 " + Thread.currentThread().getName() + " 正在把數據: " + data1 + " 換出去!");
Thread.sleep((long) Math.random() * 10000);
String data2 = (String) exchanger.exchange(data1);
System.out.println("線程 " + Thread.currentThread().getName() + " 換回的數據為:" + data2);
} catch (Exception e) {
}
}
});
}
}
線程 pool⑴-thread⑴ 正在把數據: aaa 換出去!
線程 pool⑴-thread⑵ 正在把數據: bbb 換出去!
線程 pool⑴-thread⑴ 換回的數據為:bbb
線程 pool⑴-thread⑵ 換回的數據為:aaa
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈