細數Android開源專案中那些頻繁使用的併發庫中的類

希爾瓦娜斯女神發表於2015-09-09

這篇blog旨在幫助大家 梳理一下前面分析的那些開原始碼中喜歡使用的一些類,這對我們真正理解這些專案是有極大好處的,以後遇到類似問題 我們就可以自己模仿他們也寫

出類似的程式碼。

 

1.ExecutorService

這個類實際上就是一個介面

1 public interface ExecutorService extends Executor {

我們可以看看有哪些頻繁使用的類 是實現了這個介面的,其實主要就是3個。

 1  /**
 2      * Creates a thread pool that reuses a fixed number of threads
 3      * operating off a shared unbounded queue.  At any point, at most
 4      * {@code nThreads} threads will be active processing tasks.
 5      * If additional tasks are submitted when all threads are active,
 6      * they will wait in the queue until a thread is available.
 7      * If any thread terminates due to a failure during execution
 8      * prior to shutdown, a new one will take its place if needed to
 9      * execute subsequent tasks.  The threads in the pool will exist
10      * until it is explicitly {@link ExecutorService#shutdown shutdown}.
11      *
12      * @param nThreads the number of threads in the pool
13      * @return the newly created thread pool
14      * @throws IllegalArgumentException if {@code nThreads <= 0}
15      */
16     public static ExecutorService newFixedThreadPool(int nThreads) {
17         return new ThreadPoolExecutor(nThreads, nThreads,
18                                       0L, TimeUnit.MILLISECONDS,
19                                       new LinkedBlockingQueue<Runnable>());
20     }

這個執行緒池,就是有固定執行緒數的一個執行緒池,有共享的無界佇列來執行這些執行緒。

 

 1 /**
 2      * Creates a thread pool that creates new threads as needed, but
 3      * will reuse previously constructed threads when they are
 4      * available.  These pools will typically improve the performance
 5      * of programs that execute many short-lived asynchronous tasks.
 6      * Calls to {@code execute} will reuse previously constructed
 7      * threads if available. If no existing thread is available, a new
 8      * thread will be created and added to the pool. Threads that have
 9      * not been used for sixty seconds are terminated and removed from
10      * the cache. Thus, a pool that remains idle for long enough will
11      * not consume any resources. Note that pools with similar
12      * properties but different details (for example, timeout parameters)
13      * may be created using {@link ThreadPoolExecutor} constructors.
14      *
15      * @return the newly created thread pool
16      */
17     public static ExecutorService newCachedThreadPool() {
18         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
19                                       60L, TimeUnit.SECONDS,
20                                       new SynchronousQueue<Runnable>());
21     }

這個執行緒池,是根據需要來建立這些執行緒的,但是以前構造過的執行緒 必要時可以重用他們,所以這個在很多android的開源專案裡都有用到,很頻繁,對於執行很多短期的非同步任務來說,這個執行緒池可以極大的提高程式的效能。

 

 1 /**
 2      * Creates an Executor that uses a single worker thread operating
 3      * off an unbounded queue. (Note however that if this single
 4      * thread terminates due to a failure during execution prior to
 5      * shutdown, a new one will take its place if needed to execute
 6      * subsequent tasks.)  Tasks are guaranteed to execute
 7      * sequentially, and no more than one task will be active at any
 8      * given time. Unlike the otherwise equivalent
 9      * {@code newFixedThreadPool(1)} the returned executor is
10      * guaranteed not to be reconfigurable to use additional threads.
11      *
12      * @return the newly created single-threaded Executor
13      */
14     public static ExecutorService newSingleThreadExecutor() {
15         return new FinalizableDelegatedExecutorService
16             (new ThreadPoolExecutor(1, 1,
17                                     0L, TimeUnit.MILLISECONDS,
18                                     new LinkedBlockingQueue<Runnable>()));
19     }

而這個執行緒池就比較特殊一點,他只有一個worker執行緒在工作。

 

來看第一個程式:

 1 public class Test1 {
 2 
 3     public static void main(String[] args) {
 4         ExecutorService exectrorService = Executors.newFixedThreadPool(10);
 5         // execute非同步的方法去執行這個runnable 但是這種方法無法取得執行之後的返回值
 6         exectrorService.execute(new Runnable() {
 7             @Override
 8             public void run() {
 9                 // TODO Auto-generated method stub
10                 int i = 0;
11                 while (true) {
12                     try {
13                         Thread.sleep(2000);
14                     } catch (InterruptedException e) {
15                         // TODO Auto-generated catch block
16                         e.printStackTrace();
17                     }
18                     System.out.println(i);
19                     i++;
20                 }
21             }
22 
23         });
24 
25         exectrorService.execute(new Runnable() {
26             @Override
27             public void run() {
28                 // TODO Auto-generated method stub
29                 int i = 100;
30                 while (true) {
31                     try {
32                         Thread.sleep(2000);
33                     } catch (InterruptedException e) {
34                         // TODO Auto-generated catch block
35                         e.printStackTrace();
36                     }
37                     System.out.println(i);
38                     i++;
39                 }
40             }
41 
42         });

很簡單 沒有什麼好說的只是為了演示一下這個方法,繼續往下看:

 1 public class Test1 {
 2 
 3     public static void main(String[] args) {
 4         ExecutorService exectrorService = Executors.newFixedThreadPool(10);
 5         Future future = exectrorService.submit(new Runnable() {
 6 
 7             @Override
 8             public void run() {
 9                 System.out.println("thread start");
10                 // TODO Auto-generated method stub
11                 try {
12                     Thread.sleep(13000);
13                 } catch (InterruptedException e) {
14                     // TODO Auto-generated catch block
15                     e.printStackTrace();
16                 }
17                 System.out.println("task done");
18             }
19         });
20         System.out.println("ready to print status");
21         try {
22             // 執行完畢以後才會返回null,如果執行緒還沒有執行完畢 那這個地方會阻塞
23             System.out.println("future.get ==" + future.get());
24         } catch (InterruptedException e) {
25             // TODO Auto-generated catch block
26             e.printStackTrace();
27         } catch (ExecutionException e) {
28             // TODO Auto-generated catch block
29             e.printStackTrace();
30         }
31         System.out.println("finish ready");

這個就是為了演示get方法是個阻塞方法的 我們可以看下列印的日誌。

程式一開始執行 日誌如下:

thread start
ready to print status

當執行緒執行完畢大約過了13秒以後

才會繼續輸入日誌如下:

task done
future.get ==null
finish ready

繼續看下面的例子:

 1 package com.android.testclass;
 2 
 3 import java.util.concurrent.Callable;
 4 import java.util.concurrent.ExecutionException;
 5 import java.util.concurrent.ExecutorService;
 6 import java.util.concurrent.Executors;
 7 import java.util.concurrent.Future;
 8 
 9 public class Test1 {
10 
11     public static void main(String[] args) {
12         ExecutorService exectrorService = Executors.newFixedThreadPool(10);
13         // 這個submit方法則會保證結束以後把結果返回給future,用泛型定義的方法 你可以
14         // 用任意的object代替T
15         Future future = exectrorService.submit(new Callable<String>() {
16             @Override
17             public String call() throws Exception {
18                 // TODO Auto-generated method stub
19                 System.out.println("call start");
20 
21                 Thread.sleep(5000);
22 
23                 return "call done";
24             }
25         });
26         System.out.println("ready to print");
27         try {
28             System.out.println("future.get()" + future.get());
29         } catch (InterruptedException e) {
30             // TODO Auto-generated catch block
31             e.printStackTrace();
32         } catch (ExecutionException e) {
33             // TODO Auto-generated catch block
34             e.printStackTrace();
35         }
36         System.out.println("finish");
37 
38     }
39 }

同樣是submit方法 只不過這次我們換了一個引數 這次是callable引數,這麼做的好處就是執行完畢以後可以拿到結果了

一開始輸出:

call start
ready to print

執行緒執行完畢以後輸出:

future.get()call done
finish

然後我們繼續看invokeany這個函式:

 1 package com.android.testclass;
 2 
 3 import java.util.HashSet;
 4 import java.util.Set;
 5 import java.util.concurrent.Callable;
 6 import java.util.concurrent.ExecutionException;
 7 import java.util.concurrent.ExecutorService;
 8 import java.util.concurrent.Executors;
 9 
10 public class Test2 {
11 
12     public static void main(String[] args) {
13         ExecutorService executorService = Executors.newFixedThreadPool(10);
14         Set<Callable<String>> callables = new HashSet<Callable<String>>();
15         callables.add(new Callable<String>() {
16             @Override
17             public String call() throws Exception {
18                 // TODO Auto-generated method stub
19                 System.out.println("task 1 start");
20                 Thread.sleep(3000);
21                 return "Task 1";
22             }
23         });
24         callables.add(new Callable<String>() {
25             @Override
26             public String call() throws Exception {
27                 System.out.println("task 2 start");
28                 Thread.sleep(3000);
29                 return "Task 2";
30             }
31         });
32         callables.add(new Callable<String>() {
33             @Override
34             public String call() throws Exception {
35                 System.out.println("task 3 start");
36                 Thread.sleep(3000);
37                 return "Task 3";
38             }
39         });
40         System.out.println("ready to print");
41         try {
42             //返回某一個callable執行結束的結果,結果並不確定
43             String result = executorService.invokeAny(callables);
44             System.out.println("result==" + result);
45         } catch (InterruptedException e) {
46             // TODO Auto-generated catch block
47             e.printStackTrace();
48         } catch (ExecutionException e) {
49             // TODO Auto-generated catch block
50             e.printStackTrace();
51         }
52         System.out.println("done to print");
53 
54     }
55 }

輸出我就不放了 大家可以自己跑一下。這個函式用的比較少。

 

那下面這個invokeall函式用的就比較多了

 1 package com.android.testclass;
 2 
 3 import java.util.HashSet;
 4 import java.util.List;
 5 import java.util.Set;
 6 import java.util.concurrent.Callable;
 7 import java.util.concurrent.ExecutionException;
 8 import java.util.concurrent.ExecutorService;
 9 import java.util.concurrent.Executors;
10 import java.util.concurrent.Future;
11 
12 public class Test3 {
13 
14     public static void main(String[] args) {
15         ExecutorService executorService = Executors.newFixedThreadPool(10);
16         Set<Callable<String>> callables = new HashSet<Callable<String>>();
17         callables.add(new Callable<String>() {
18             @Override
19             public String call() throws Exception {
20                 // TODO Auto-generated method stub
21                 System.out.println("task 1 start");
22                 Thread.sleep(3000);
23                 return "Task 1";
24             }
25         });
26         callables.add(new Callable<String>() {
27             @Override
28             public String call() throws Exception {
29                 System.out.println("task 2 start");
30                 Thread.sleep(6000);
31                 return "Task 2";
32             }
33         });
34         callables.add(new Callable<String>() {
35             @Override
36             public String call() throws Exception {
37                 System.out.println("task 3 start");
38                 Thread.sleep(9000);
39                 return "Task 3";
40             }
41         });
42         System.out.println("ready to print");
43 
44         try {
45             // invoke方法也是阻塞方法,一定是所有callable都執行完畢才會返回結果
46             List<Future<String>> futures = executorService.invokeAll(callables);
47             System.out.println("invoke done");
48             for (Future<String> future : futures) {
49                 System.out.println("future.get=" + future.get());
50                 System.out.println("get done");
51             }
52             System.out.println("all get done");
53         } catch (InterruptedException e) {
54             // TODO Auto-generated catch block
55             e.printStackTrace();
56         } catch (ExecutionException e) {
57             // TODO Auto-generated catch block
58             e.printStackTrace();
59         }
60 
61     }
62 }

 

總的來說,在android裡如果你要使用執行緒池的話,那上面的這些方法 基本就肯定足夠你使用了。

 

2.ConcurrentHashMap

這個類,相信很多人都不陌生,我就略微提一下,很多人以前在單執行緒的時候使用hashmap,多執行緒的時候使用hashtable,這麼做雖然是對的,

但是hashtable裡的原始碼說明了 這是直接對整個map進行加鎖,效率是很低的,而這個concurrenthashmap的讀操作幾乎不會有鎖,

而寫操作由於採用了分段處理,所以寫操作的鎖 的概率和次數也大大降低。總體來說這是一個效率極高的 可適用於併發性的hashmap。

例子和原理 網上有很多 我這裡就不放了。

此外和他類似的還有LinkedHashMap,實現LRU的最好選擇,這個也不多講,只是提一下,網上資料很多。

 

3.PriorityBlockingQueue

這個就是優先順序佇列,當然也是支援併發的,這個佇列裡存放的物件 必須是實現了Comparable 介面的。並且小的是在這個佇列前面的 大的就一定是在佇列的後面。

比如說我們先定義一個類:

 1 package com.android.testclass;
 2 
 3 public class PriorityEntity implements Comparable<PriorityEntity> {
 4 
 5     private static int count = 0;
 6     private int id = count++;
 7     private int priority;
 8     private int index = 0;
 9 
10     public PriorityEntity(int priority, int index) {
11         // TODO Auto-generated constructor stub
12         this.priority = priority;
13         this.index = index;
14     }
15 
16     @Override
17     public String toString() {
18         return "PriorityEntity [id=" + id + ", priority=" + priority + ", index=" + index + "]";
19     }
20 
21     @Override
22     public int compareTo(PriorityEntity o) {
23         // TODO Auto-generated method stub
24         return this.priority > o.priority ? 1 : this.priority < o.priority ? -1 : 0;
25     }
26 
27 }

那個靜態變數就表示索引的,構造出一個物件 索引就加1. 然後我們來寫一下測試這個佇列的程式碼:

 1 package com.android.testclass;
 2 
 3 import java.util.Random;
 4 import java.util.concurrent.ExecutorService;
 5 import java.util.concurrent.Executors;
 6 import java.util.concurrent.PriorityBlockingQueue;
 7 import java.util.concurrent.TimeUnit;
 8 
 9 public class Test6 {
10 
11     public static void main(String[] args) {
12         // TODO Auto-generated method stub
13 
14         PriorityBlockingQueue q = new PriorityBlockingQueue<>();
15         Random r = new Random(47);
16         ExecutorService se = Executors.newCachedThreadPool();
17         //往佇列裡 放物件,priority的值是 隨即的
18         se.execute(new Runnable() {
19 
20             @Override
21             public void run() {
22                 // TODO Auto-generated method stub
23                 int i = 0;
24                 while (true) {
25                     q.put(new PriorityEntity(r.nextInt(10), i++));
26 
27                     try {
28                         TimeUnit.MILLISECONDS.sleep(r.nextInt(1000));
29                     } catch (InterruptedException e) {
30                         // TODO Auto-generated catch block
31                         e.printStackTrace();
32                     }
33 
34                 }
35             }
36         });
37         //從佇列裡 取物件,然後把佇列裡剩餘的值打出來 就會發現 每次取出來的都是最小的那個 剩下的都是從小到大排序好的
38         se.execute(new Runnable() {
39 
40             @Override
41             public void run() {
42                 // TODO Auto-generated method stub
43                 while (true) {
44                     try {
45                         System.out.println(("take-- " + q.take() + " left:-- [" + q.toString() + "]"));
46                     } catch (InterruptedException e1) {
47                         // TODO Auto-generated catch block
48                         e1.printStackTrace();
49                     }
50                     try {
51                         TimeUnit.MILLISECONDS.sleep(r.nextInt(1000));
52                     } catch (InterruptedException e) {
53                         // TODO Auto-generated catch block
54                         e.printStackTrace();
55                     }
56 
57                 }
58             }
59         });
60 
61     }
62 
63 }

擷取一段日誌 可以得到我們註釋裡的結論:

 1 take-- PriorityEntity  [priority=8, index=0] left:-- [[]]
 2 take-- PriorityEntity  [priority=1, index=1] left:-- [[]]
 3 take-- PriorityEntity  [priority=8, index=2] left:-- [[]]
 4 take-- PriorityEntity  [priority=7, index=3] left:-- [[PriorityEntity  [priority=8, index=4]]]
 5 take-- PriorityEntity  [priority=8, index=4] left:-- [[PriorityEntity  [priority=9, index=5]]]
 6 take-- PriorityEntity  [priority=1, index=6] left:-- [[PriorityEntity  [priority=8, index=7], PriorityEntity  [priority=9, index=5]]]
 7 take-- PriorityEntity  [priority=8, index=7] left:-- [[PriorityEntity  [priority=9, index=5]]]
 8 take-- PriorityEntity  [priority=2, index=8] left:-- [[PriorityEntity  [priority=9, index=5]]]
 9 take-- PriorityEntity  [priority=9, index=5] left:-- [[]]
10 take-- PriorityEntity  [priority=5, index=9] left:-- [[]]
11 take-- PriorityEntity  [priority=4, index=10] left:-- [[]]
12 take-- PriorityEntity  [priority=4, index=13] left:-- [[PriorityEntity  [priority=6, index=11], PriorityEntity  [priority=6, index=12]]]
13 take-- PriorityEntity  [priority=3, index=14] left:-- [[PriorityEntity  [priority=6, index=16], PriorityEntity  [priority=6, index=12], PriorityEntity  [priority=6, index=11], PriorityEntity  [priority=8, index=15]]]
14 take-- PriorityEntity  [priority=6, index=16] left:-- [[PriorityEntity  [priority=6, index=12], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=6, index=11]]]
15 take-- PriorityEntity  [priority=6, index=12] left:-- [[PriorityEntity  [priority=6, index=17], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=6, index=11]]]
16 take-- PriorityEntity  [priority=6, index=17] left:-- [[PriorityEntity  [priority=6, index=11], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=8, index=18]]]
17 take-- PriorityEntity  [priority=6, index=11] left:-- [[PriorityEntity  [priority=8, index=18], PriorityEntity  [priority=8, index=15]]]
18 take-- PriorityEntity  [priority=4, index=19] left:-- [[PriorityEntity  [priority=8, index=18], PriorityEntity  [priority=8, index=15]]]
19 take-- PriorityEntity  [priority=8, index=18] left:-- [[PriorityEntity  [priority=8, index=15]]]
20 take-- PriorityEntity  [priority=7, index=20] left:-- [[PriorityEntity  [priority=8, index=15]]]
21 take-- PriorityEntity  [priority=2, index=21] left:-- [[PriorityEntity  [priority=4, index=22], PriorityEntity  [priority=8, index=15]]]
22 take-- PriorityEntity  [priority=4, index=22] left:-- [[PriorityEntity  [priority=8, index=23], PriorityEntity  [priority=8, index=15]]]
23 take-- PriorityEntity  [priority=8, index=23] left:-- [[PriorityEntity  [priority=8, index=15]]]
24 take-- PriorityEntity  [priority=5, index=24] left:-- [[PriorityEntity  [priority=8, index=15]]]
25 take-- PriorityEntity  [priority=2, index=25] left:-- [[PriorityEntity  [priority=8, index=26], PriorityEntity  [priority=8, index=15]]]
26 take-- PriorityEntity  [priority=3, index=27] left:-- [[PriorityEntity  [priority=4, index=28], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=8, index=26]]]
27 take-- PriorityEntity  [priority=1, index=30] left:-- [[PriorityEntity  [priority=4, index=28], PriorityEntity  [priority=7, index=29], PriorityEntity  [priority=8, index=26], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=8, index=31]]]
28 take-- PriorityEntity  [priority=4, index=28] left:-- [[PriorityEntity  [priority=7, index=29], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=8, index=26], PriorityEntity  [priority=9, index=32], PriorityEntity  [priority=8, index=31]]]

有興趣的話可以看看java裡面 有幾種類 都實現了AbstractQueue,可以挑選出適合自己業務裡的佇列,減少開發難度

1 public abstract class AbstractQueue<E>
2     extends AbstractCollection<E>
3     implements Queue<E> {

 

4.CopyOnWriteArrayList

考慮這樣一種場景,一個list,被好幾個執行緒同時讀寫,那一般都會報錯。

 1 Exception in thread "pool-1-thread-7" java.util.ConcurrentModificationException
 2     at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
 3     at java.util.ArrayList$Itr.next(Unknown Source)
 4     at com.android.testclass.Test7$ReadTask.run(Test7.java:35)
 5     at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
 6     at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
 7     at java.lang.Thread.run(Unknown Source)
 8 Exception in thread "pool-1-thread-6" java.util.ConcurrentModificationException
 9     at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
10     at java.util.ArrayList$Itr.next(Unknown Source)
11     at com.android.testclass.Test7$ReadTask.run(Test7.java:35)
12     at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
13     at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
14     at java.lang.Thread.run(Unknown Source)

於是很多人就喜歡用Collections.synchronizedList() 來處理,但是這樣做在很多時候效率是低的,比如

假設現在告訴你,你需要設計一個快取list,你就應該使用CopyOnWrite這個類了,因為快取大家都知道,讀操作比較多,而寫操作除了在初始建立快取的階段,其他時候很少使用。

他的原理也很簡單,就是你在用迭代器寫操作的時候 是把原來的資料拷貝了一份映象在記憶體中,而你在讀的時候 是讀的本體,寫操作寫完以後才會覆蓋掉原來的本地。所以可以

得知 這個類對於頻繁讀的同步性list 是非常有效的。使用方法也很簡單。

1         List<String> list = new CopyOnWriteArrayList<String>();

 

5.ThreadLocal

這個類也是很有效,很多開源作者喜歡用的一個類,他主要的作用是為每個執行緒創造一個變數的副本互相不會影響。很多人不理解這句話,

對於多執行緒操作來說 分為兩種

1 第一種,執行緒和執行緒之間互相讀取操作,比如全域性的計數器這種,a執行緒要加,b執行緒也要加,每次加的時候 都要讀取最新的計數器的狀態。這是最常見的一種同步操作。

2 第二種,session,session一個使用者一個,互相不影響,大家維持自己的就可以,他的目標就是a的seesion a自己操作 儲存 讀取,b的seesion也是自己維護,和其他人無關。

換一句話說 如果你需要多個執行緒之間通訊,那就用同步機制,

如果你不需要執行緒與執行緒之間通訊,只要互相別影響 不讓他們發生衝突 則threadlocal是最佳選擇。

 1 package com.android.testclass;
 2 
 3 public class Test8 {
 4 
 5     static final ThreadLocal<Integer> local = new ThreadLocal<Integer>() {
 6 
 7         protected Integer initialValue() {
 8 
 9             return 0;
10         };
11 
12     };
13 
14     public static void main(String[] args) {
15         // TODO Auto-generated method stub
16 
17         Thread[] threads = new Thread[5];
18         for (int i = 0; i < 5; i++) {
19             threads[i] = new Thread(new Runnable() {
20 
21                 @Override
22                 public void run() {
23                     // TODO Auto-generated method stub
24 
25                     int num = local.get();
26                     for (int i = 0; i < 5; i++) {
27                         num++;
28                     }
29                     local.set(num);
30                     System.out.println(Thread.currentThread().getName() + " : " + local.get());
31 
32                 }
33             }, "thread-" + i);
34         }
35 
36         for (Thread thread : threads) {
37             thread.start();
38         }
39 
40     }
41 
42 }

看下輸出

1 thread-0 : 5
2 thread-4 : 5
3 thread-1 : 5
4 thread-3 : 5
5 thread-2 : 5

 

接著看下面的

 1 package com.android.testclass;
 2 
 3 public class Test9 {
 4 
 5     private static Index num = new Index();
 6     // 建立一個Index型別的本地變數
 7     private static ThreadLocal<Index> local = new ThreadLocal<Index>() {
 8         @Override
 9         protected Index initialValue() {
10             return num;
11         }
12     };
13 
14     public static void main(String[] args) throws InterruptedException {
15         Thread[] threads = new Thread[5];
16         for (int j = 0; j < 5; j++) {
17             threads[j] = new Thread(new Runnable() {
18                 @Override
19                 public void run() {
20                     // 取出當前執行緒的本地變數,並累加1000次
21                     Index index = local.get();
22                     for (int i = 0; i < 1000; i++) {
23                         index.increase();
24                     }
25                     System.out.println(Thread.currentThread().getName() + " : " + index.num);
26 
27                 }
28             }, "Thread-" + j);
29         }
30         for (Thread thread : threads) {
31             thread.start();
32         }
33     }
34 
35     static class Index {
36         int num;
37 
38         public void increase() {
39             num++;
40         }
41     }
42 
43 }

看輸出

Thread-1 : 2594
Thread-4 : 3594
Thread-2 : 2594
Thread-0 : 2594
Thread-3 : 4594

是因為第10行,那邊放的是一個靜態變數的引用,所以輸出的結果不是我們想象的

其實只要改成

 

1 private static ThreadLocal<Index> local = new ThreadLocal<Index>() {
2         @Override
3         protected Index initialValue() {
4             return new Index();
5         }
6     };

結果就是正確的:

1 Thread-2 : 1000
2 Thread-3 : 1000
3 Thread-0 : 1000
4 Thread-4 : 1000
5 Thread-1 : 1000

 

相關文章