java多線程學習-java.util.concurrent詳解(二)Semaphore/FutureTask/Exchanger
3. Semaphore
我們先來學習一下JDK1.5 API中關于這個類的詳細介紹: “一個計數信號量。從概念上講,信號量維護了一個許可集。如有必要,在許可可用前會阻塞每一個 acquire(),然后再獲取該許可。每個 release() 添加一個許可,從而可能釋放一個正在阻塞的獲取者。但是,不使用實際的許可對象,Semaphore 只對可用許可的號碼進行計數,并采取相應的行動。” 我們一般用它來控制某個對象的線程訪問對象 例如,對于某個容器,我們規定,最多只能容納n個線程同時操作 使用信號量來模擬實現具體代碼如下(參考 [JCip])
import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class TestSemaphore { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); TestSemaphore t = new TestSemaphore(); final BoundedHashSet<String> set = t.getSet(); for (int i = 0; i < 3; i++) {//三個線程同時操作add exec.execute(new Runnable() { public void run() { try { set.add(Thread.currentThread().getName()); } catch (InterruptedException e) { e.PRintStackTrace(); } } }); } for (int j = 0; j < 3; j++) {//三個線程同時操作remove exec.execute(new Runnable() { public void run() { set.remove(Thread.currentThread().getName()); } }); } exec.shutdown(); } public BoundedHashSet<String> getSet() { return new BoundedHashSet<String>(2);//定義一個邊界約束為2的線程 } class BoundedHashSet<T> { private final Set<T> set; private final Semaphore semaphore; public BoundedHashSet(int bound) { this.set = Collections.synchronizedSet(new HashSet<T>()); this.semaphore = new Semaphore(bound, true); } public void add(T o) throws InterruptedException { semaphore.acquire();//信號量控制可訪問的線程數目 set.add(o); System.out.printf("add:%s%n",o); } public void remove(T o) { if (set.remove(o)) semaphore.release();//釋放掉信號量 System.out.printf("remove:%s%n",o); } } } 總結:Semaphore通常用于對象池的控制4.FutureTask 我們先來學習一下JDK1.5 API中關于這個類的詳細介紹: “取消的異步計算。利用開始和取消計算的方法、查詢計算是否完成的方法和獲取計算結果的方法,此類提供了對 Future 的基本實現。僅在計算完成時才能獲取結果;如果計算尚未完成,則阻塞 get 方法。一旦計算完成,就不能再重新開始或取消計算。 可使用 FutureTask 包裝 Callable 或 Runnable 對象。因為 FutureTask 實現了 Runnable,所以可將 FutureTask 提交給 Executor 執行。 除了作為一個獨立的類外,此類還提供了 protected 功能,這在創建自定義任務類時可能很有用。 “ 應用舉例:我們的算法中有一個很耗時的操作,在編程的是,我們希望將它獨立成一個模塊,調用的時候當做它是立刻返回的,并且可以隨時取消的 具體代碼如下(參考 [JCIP])
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; public class TestFutureTask { public static void main(String[] args) { ExecutorService exec=Executors.newCachedThreadPool(); FutureTask<String> task=new FutureTask<String>(new Callable<String>(){//FutrueTask的構造參數是一個Callable接口 @Override public String call() throws Exception { return Thread.currentThread().getName();//這里可以是一個異步操作 }}); try { exec.execute(task);//FutureTask實際上也是一個線程 String result=task.get();//取得異步計算的結果,如果沒有返回,就會一直阻塞等待 System.out.printf("get:%s%n",result); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } 總結:FutureTask其實就是新建了一個線程單獨執行,使得線程有一個返回值,方便程序的編寫5. Exchanger 我們先來學習一下JDK1.5 API中關于這個類的詳細介紹: “可以在pair中對元素進行配對和交換的線程的同步點。每個線程將條目上的某個方法呈現給 exchange 方法,與伙伴線程進行匹配,并且在返回時接收其伙伴的對象。Exchanger 可能被視為 SynchronousQueue 的雙向形式。Exchanger 可能在應用程序(比如遺傳算法和管道設計)中很有用。 “ 應用舉例:有兩個緩存區,兩個線程分別向兩個緩存區fill和take,當且僅當一個滿了,兩個緩存區交換 代碼如下(參考了網上給的示例 http://hi.baidu.com/webidea/blog/item/2995e731e53ad5a55fdf0e7d.html)
import java.util.ArrayList; import java.util.concurrent.Exchanger; public class TestExchanger { public static void main(String[] args) { final Exchanger<ArrayList<Integer>> exchanger = new Exchanger<ArrayList<Integer>>(); final ArrayList<Integer> buff1 = new ArrayList<Integer>(10); final ArrayList<Integer> buff2 = new ArrayList<Integer>(10); new Thread(new Runnable() { @Override public void run() { ArrayList<Integer> buff = buff1; try { while (true) { if (buff.size() >= 10) { buff = exchanger.exchange(buff);//開始跟另外一個線程交互數據 System.out.println("exchange buff1"); buff.clear(); } buff.add((int)(Math.random()*100)); Thread.sleep((long)(Math.random()*1000)); } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable(){ @Override public void run() { ArrayList<Integer> buff=buff2; while(true){ try { for(Integer i:buff){ System.out.println(i); } Thread.sleep(1000); buff=exchanger.exchange(buff);//開始跟另外一個線程交換數據 System.out.println("exchange buff2"); } catch (InterruptedException e) { e.printStackTrace(); } } }}).start(); } } 總結:Exchanger在特定的使用場景比較有用(兩個伙伴線程之間的數據交互)
新聞熱點
疑難解答