解決併發情況下的容器線程安全問題的。給多線程環境準備一個線程安全的容器對象。
線程安全的容器對象: Vector, Hashtable。線程安全容器對象,都是使用 synchronized 方法實現的。
concurrent 包中的同步容器,大多數是使用系統底層技術實現的線程安全。類似 native。 Java8 中使用 CAS。
1、Map/Set
1.1 ConcurrentHashMap/ConcurrentHashSet
底層哈希實現的同步 Map(Set)。效率高,線程安全。使用系統底層技術實現線程安全。 量級較 synchronized 低。key 和 value 不能爲 null。
1.2 ConcurrentSkipListMap/ConcurrentSkipListSet
底層跳錶(SkipList)實現的同步 Map(Set)。有序,效率比 ConcurrentHashMap 稍低。
import java.util.Map; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; public class Test_01_ConcurrentMap { public static void main(String[] args) { // final Map<String, String> map = new Hashtable<>(); final Map<String, String> map = new ConcurrentHashMap<>(); //ConcurrentSkipListMap跳錶實現的,是排序的,最慢 // final Map<String, String> map = new ConcurrentSkipListMap<>(); final Random r = new Random(); Thread[] array = new Thread[100]; final CountDownLatch latch = new CountDownLatch(array.length); long begin = System.currentTimeMillis(); for (int i = 0; i < array.length; i++) { array[i] = new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 100000; j++) { map.put("key" + r.nextInt(100000), "value" + r.nextInt(100000)); } latch.countDown(); } }); } for (Thread t : array) { t.start(); } try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("執行時間爲 : " + (end - begin) + "毫秒!"); } }
2、List
2.1 CopyOnWriteArrayList
寫時複製集合。寫入效率低,讀取效率高。每次寫入數據,都會創建一個新的底層數組。
import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.CountDownLatch; public class Test_02_CopyOnWriteList { public static void main(String[] args) { final List<String> list = new ArrayList<>(); // final List<String> list = new Vector<>(); // final List<String> list = new CopyOnWriteArrayList<>(); final Random r = new Random(); Thread[] array = new Thread[100]; final CountDownLatch latch = new CountDownLatch(array.length); long begin = System.currentTimeMillis(); for (int i = 0; i < array.length; i++) { array[i] = new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 1000; j++) { list.add("value" + r.nextInt(100000)); } latch.countDown(); } }); } for (Thread t : array) { t.start(); } try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("執行時間爲 : " + (end - begin) + "毫秒!"); System.out.println("List.size() : " + list.size()); } }
3、Queue
3.1 ConcurrentLinkedQueue
基礎鏈表同步隊列。
/** * 併發容器 - ConcurrentLinkedQueue * 隊列 - 鏈表實現的。 */ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; public class Test_03_ConcurrentLinkedQueue { public static void main(String[] args) { Queue<String> queue = new ConcurrentLinkedQueue<>(); for (int i = 0; i < 10; i++) { queue.offer("value" + i); } System.out.println(queue); System.out.println(queue.size()); // peek() -> 查看queue中的首數據 System.out.println(queue.peek()); System.out.println(queue.size()); // poll() -> 獲取queue中的首數據 System.out.println(queue.poll()); System.out.println(queue.size()); } }
3.2 LinkedBlockingQueue
阻塞隊列,隊列容量不足自動阻塞,隊列容量爲 0 自動阻塞。
/** * 併發容器 - LinkedBlockingQueue * 阻塞容器。 * put & take - 自動阻塞。 * put自動阻塞, 隊列容量滿後,自動阻塞 * take自動阻塞方法, 隊列容量爲0後,自動阻塞。 */ import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; public class Test_04_LinkedBlockingQueue { final BlockingQueue<String> queue = new LinkedBlockingQueue<>(); final Random r = new Random(); public static void main(String[] args) { final Test_04_LinkedBlockingQueue t = new Test_04_LinkedBlockingQueue(); new Thread(new Runnable() { @Override public void run() { while (true) { try { t.queue.put("value" + t.r.nextInt(1000)); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } }, "producer").start(); for (int i = 0; i < 3; i++) { new Thread(new Runnable() { @Override public void run() { while (true) { try { System.out.println(Thread.currentThread().getName() + " - " + t.queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } } } }, "consumer" + i).start(); } } }
3.3 ArrayBlockingQueue
底層數組實現的有界隊列。自動阻塞。根據調用 API(add/put/offer)不同,有不同特 性。
當容量不足的時候,有阻塞能力。
add 方法在容量不足的時候,拋出異常。put 方法在容量不足的時候,阻塞等待。
offer 方法:
單參數 offer 方法,不阻塞。容量不足的時候,返回 false。當前新增數據操作放棄。 三參數 offer 方法(offer(value,times,timeunit)),容量不足的時候,阻塞 times 時長(單
位爲 timeunit),如果在阻塞時長內,有容量空閒,新增數據返回 true。如果阻塞時長範圍 內,無容量空閒,放棄新增數據,返回 false。
/** * 併發容器 - ArrayBlockingQueue * 有界容器。 */ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; public class Test_05_ArrayBlockingQueue { final BlockingQueue<String> queue = new ArrayBlockingQueue<>(3); public static void main(String[] args) { final Test_05_ArrayBlockingQueue t = new Test_05_ArrayBlockingQueue(); for (int i = 0; i < 5; i++) { // System.out.println("add method : " + t.queue.add("value"+i)); /*try { t.queue.put("put"+i); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("put method : " + i);*/ // System.out.println("offer method : " + t.queue.offer("value"+i)); try { System.out.println("offer method : " + t.queue.offer("value" + i, 1, TimeUnit.SECONDS)); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(t.queue); } }
3.4 DelayQueue
延時隊列。根據比較機制,實現自定義處理順序的隊列。常用於定時任務。
如:定時關機。
/** * 併發容器 - DelayQueue * 無界容器。 */ import java.util.concurrent.BlockingQueue; import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; public class Test_06_DelayQueue { static BlockingQueue<MyTask_06> queue = new DelayQueue<>(); public static void main(String[] args) throws InterruptedException { long value = System.currentTimeMillis(); MyTask_06 task1 = new MyTask_06(value + 2000); MyTask_06 task2 = new MyTask_06(value + 1000); MyTask_06 task3 = new MyTask_06(value + 3000); MyTask_06 task4 = new MyTask_06(value + 2500); MyTask_06 task5 = new MyTask_06(value + 1500); queue.put(task1); queue.put(task2); queue.put(task3); queue.put(task4); queue.put(task5); System.out.println(queue); System.out.println(value); for (int i = 0; i < 5; i++) { System.out.println(queue.take()); } } } class MyTask_06 implements Delayed { private long compareValue; public MyTask_06(long compareValue) { this.compareValue = compareValue; } /** * 比較大小。自動實現升序 * 建議和getDelay方法配合完成。 * 如果在DelayQueue是需要按時間完成的計劃任務,必須配合getDelay方法完成。 */ @Override public int compareTo(Delayed o) { return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS)); } /** * 獲取計劃時長的方法。 * 根據參數TimeUnit來決定,如何返回結果值。 */ @Override public long getDelay(TimeUnit unit) { return unit.convert(compareValue - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override public String toString() { return "Task compare value is : " + this.compareValue; } }
3.5 LinkedTransferQueue
轉移隊列,是一個容量爲 0 的隊列。使用 transfer 方法,實現數據的即時處理。沒有消費者,就阻塞。
/** * 併發容器 - LinkedTransferQueue * 轉移隊列 * add - 隊列會保存數據,不做阻塞等待。 * transfer - 是TransferQueue的特有方法。必須有消費者(take()方法的調用者)。 * 如果沒有任意線程消費數據,transfer方法阻塞。一般用於處理即時消息。 */ import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TransferQueue; public class Test_07_TransferQueue { TransferQueue<String> queue = new LinkedTransferQueue<>(); public static void main(String[] args) { final Test_07_TransferQueue t = new Test_07_TransferQueue(); /*new Thread(new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName() + " thread begin " ); System.out.println(Thread.currentThread().getName() + " - " + t.queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } } }, "output thread").start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } try { t.queue.transfer("test string"); } catch (InterruptedException e) { e.printStackTrace(); }*/ new Thread(new Runnable() { @Override public void run() { try { t.queue.transfer("test string"); // t.queue.add("test string"); System.out.println("add ok"); } catch (Exception e) { e.printStackTrace(); } } }).start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName() + " thread begin "); System.out.println(Thread.currentThread().getName() + " - " + t.queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } } }, "output thread").start(); } }
3.6 SynchronusQueue
同步隊列,是一個容量爲 0 的隊列。是一個特殊的 TransferQueue。 必須現有消費線程等待,才能使用的隊列。
add 方法,無阻塞。若沒有消費線程阻塞等待數據,則拋出異常。 put 方法,有阻塞。若沒有消費線程阻塞等待數據,則阻塞。
/** * 併發容器 - SynchronousQueue */ import java.util.concurrent.BlockingQueue; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; public class Test_08_SynchronusQueue { BlockingQueue<String> queue = new SynchronousQueue<>(); public static void main(String[] args) { final Test_08_SynchronusQueue t = new Test_08_SynchronusQueue(); new Thread(new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName() + " thread begin "); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " - " + t.queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } } }, "output thread").start(); /*try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }*/ // t.queue.add("test add"); try { t.queue.put("test put"); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " queue size : " + t.queue.size()); } }