Java爲開發提供了不少有用的工具類,這些工具類能夠幫助咱們更加高效的編寫併發程序,本篇咱們將介紹這些實用工具的用法。安全
ThreadLocal類用於解決多線程共享一個變量的問題,當多線程訪問同一個變量時可能會致使結果的錯誤,防止這種錯誤第一種辦法就是使用鎖來保護對象;第二種方法就是完全根除共享,即每一個線程訪問本身私有的變量。有的同窗會以爲第二種方法就會有一些侷限性,由於有些時候不得不共享同一個變量。是的確實有侷限性,可是在不少狀況下是能夠不共享變量就能達到一樣的效果,ThreadLocal就是爲了解決這一問題而設計的。多線程
ThreadLocal使用方法以下:併發
class IncreaseThread implements Runnable { public void run() { for(int i=0; i< 10000; i++) { TLTest.number.set(TLTest.number.get() + 1); } //如下爲彙總代碼 synchronized(TLTest.result) { TLTest.result += TLTest.number.get(); } } } public class TLTest { public static ThreadLocal<Integer> number; public static Integer result = 0; public static void main(String[] args) throws Exception { number = new ThreadLocal<Integer>() { public Integer initialValue() { return 0; } }; ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new IncreaseThread()); exec.execute(new IncreaseThread()); exec.shutdown(); Thread.sleep(500); System.out.println("result: " + result); } }
輸出結果以下:dom
result: 20000工具
在TLTest類中咱們定義了ThreadLocal變量和Integer變量,ThreadLocal變量須要爲其建立一個匿名內部類來實現爲其指定初始值,咱們將初始值指定爲0。咱們定義了一個線程類,這個線程負責將ThreadLocal的值加10000,最後線程會將本身的計算結果彙總到TLTest.result變量中。這個過程當中雖然咱們建立的兩個線程都對同一個ThreadLocal變量進行操做,可是沒有致使計算結果出錯。由於ThreadLocal爲每個線程分配了不一樣的存儲空間,咱們能夠簡單的將其理解爲一個線程對象和值的Map<Thread,Integer>(注意:只是能夠這麼理解,但實際上不是)。測試
CountDownLatch用於線程間的合做,其使用方法和wait()/notify()相似,CountDownLatch類有兩個方法:countDown()和await()方法,在建立CountDownLatch的對象時爲其指定countDown()方法調用的次數,當調用await()方法時當前線程會一直被阻塞,直到countDown()方法被調用了指定的次數。設想一種狀況,一個工頭在接到任務時會把任務分發給不一樣的工人,只有當全部的工人都完成本身的工做的時候,工頭才能夠交工。this
咱們用代碼模擬一下這種狀況:spa
class Worker implements Runnable { private int id; public Worker(int id) { this.id = id; } public void run() { Random rand = new Random(); int workTime = rand.nextInt(1000); System.out.println(id + ": 開始幹活"); try { Thread.sleep(workTime); } catch (Exception e) {} System.out.println(id + ": 完成了"); CDLTest.cdl.countDown(); } } public class CDLTest { private static int numberOfWorker = 3; public static CountDownLatch cdl = new CountDownLatch(numberOfWorker); public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool(); for(int i=0; i<numberOfWorker; i++) { exec.execute(new Worker(i)); } exec.shutdown(); cdl.await(); System.out.println("工頭:交工"); } }
輸出結果以下:線程
1: 開始幹活設計
2: 開始幹活
0: 開始幹活
1: 完成了
2: 完成了
0: 完成了
工頭:交工
在本例中主線程承擔工頭的角色,調用await()方法等待工人線程完成工做。咱們還經過線程池建立了3個工人線程,咱們使用隨機數讓每一個線程隨機睡眠0-1000毫秒,用來模擬工人工做。
每一個工人完成本身的任務後調用countDown()方法,當全部的工人線程都作完本身的工做後主線程就能夠「交工」了。
PriorityBlockingQueue和前面講過的LinkedBlockingQueue、ArrayBlockingQueue類似,他們都實現了BlockingQueue接口,可是PriorityBlockingQueue和它們最大的區別是這個隊列每次取出的都是「優先級」最高的,而不是最早進入的。所以要想實現它的優先級的特性,容器中的元素必須實現了Comparable接口,不然容器將拋ClassCastException異常。此外PriorityBlockingQueue也是線程安全的,所以使用的時候不用加鎖。因爲以前咱們測試過LinkedBlockingQueue的阻塞性,所以PriorityBlockingQueue的阻塞性咱們就不測試了,簡單的測試一下它的「優先級」的性質:
public class PBQTest { public static void main(String[] args) throws InterruptedException { BlockingQueue<String> pbq = new PriorityBlockingQueue<String>(); String[] strs = new String[]{"C", "A", "B"}; for(int i=0;i <strs.length;i++) { pbq.add(strs[i]); } while(!pbq.isEmpty()) { System.out.println(pbq.take()); } } }
輸出結果以下:
A
B
C
String類實現了Comparable接口,根據字母順序比較字符串的大小。咱們向隊列中添加元素的順序是"C", "A", "B",而取出順序是"A", "B", "C",此因能夠看出其「優先級」的性質。
本篇介紹了三個經常使用的工具類,ThreadLocal用於解決多線程共享同一個變量的問題,它至關於建立了一個以線程對象爲key以目標對象爲value的一個Map,但實際上和Map是有區別的,好比Map對象不會在某個線程退出後對相應的value作垃圾回收,而ThreadLocal會對其進行回收。CountDownLatch用於同步多個任務,讓某些任務等待其它任務執行的一組操做,須要注意的是能夠有多個線程調用await()方法,當調用countDown()的次數到達指定數量的時候全部調用await()方法的線程都會從阻塞狀態變爲運行狀態。PriorityBlockingQueue的用法和其它實現BlockingQueue接口的用法類似,只是PriorityBlockingQueue中的元素的取出順序是按照優先級排序的。
公衆號:今日說碼。關注個人公衆號,可查看連載文章。遇到不理解的問題,直接在公衆號留言便可。