1. 程式人生 > >Java 併發程式設計(九)併發集合框架

Java 併發程式設計(九)併發集合框架

集合框架簡介

程式設計中,我們經常需要集中存放多個數據。陣列是我們的一個很好的選擇,前提是我們事先明確我們將要儲存物件的數量。陣列在初始化時如果指定了長度,那這個陣列長度就是不可變的了,如果我們需要儲存一個可以動態增長的資料(編譯時無法確定具體的物件數量),所以Java提供了集合框架來實現這個功能。

集合類主要負責儲存資料,因此集合類也被稱為容器類。Java中集合類都位於java.util包下,後來為了處理多執行緒併發安全問題,Java5在java.util.concurrent包下提供了支援多執行緒併發的集合類。

Java容器類的用途是”儲存物件”,並將其劃分為兩個不同的概念:

  1. Collection
    一組”對立”的元素,通常這些元素都服從某種規則
      1.1) List必須保持元素特定的順序
      1.2) Set不能有重複元素
      1.3) Queue保持一個佇列(先進先出)的順序
  2. Map
    一組成對的”鍵值對”物件

Collection和Map的區別在於容器中每個位置儲存的元素個數:

  • Collection 每個位置只能儲存一個元素(物件)
  • Map儲存的是”鍵值對”,我們可以通過”鍵”找到該鍵對應的”值”

Java集合框架Collection圖示:

這裡寫圖片描述

Map集合框架圖示:
這裡寫圖片描述

Java併發集合框架

我們著重說明一下Java併發的集合框架,就是java.util.concurrent包下的集合類

非阻塞佇列

非阻塞佇列就是在佇列中沒有資料時,對此佇列的操作將出現異常或者返回null,無需等待/阻塞的特色。

在JDK的併發包中,常見的非阻塞佇列有以下幾個:
- ConcurrentHashMap
- ConcurrentSkipListMap
- ConcurrentSkipListSet
- ConcurrentLinkedQueue
- ConcurrentLinkedDeque
- CopyOnWriteArrayList
- CopyOnWriteArraySet

ConcurrentHashMap類

這個類是支援併發操作的Map物件,與之對應的不支援併發操作的Map物件是HashMap,Hashtable物件也是Map的一個子類,也是支援併發操作,但是其不支援Iterator併發的刪除操作,如果程式中要對map集合做併發以及Iterator併發編輯/刪除操作,推薦使用ConcurrentHashMap集合類。

HashMap集合不支援併發示例:

package com.collections.hashmapdemo;

import java.util.HashMap;

public class HashMapDemo {
    public
static HashMap hashMap = new HashMap(); public static void main(String[] args) { Thread threadA = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 50000; i++) { hashMap.put("ThreadA" + (i + 1), "ThreadA" + i + 1); System.out.println("ThreadA" + (i + 1)); } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 50000; i++) { hashMap.put("ThreadB" + (i + 1), "ThreadB" + i + 1); System.out.println("ThreadB" + (i + 1)); } } }); threadA.start(); threadB.start(); } }

執行結果
有時候程式可以正常執行完,有時候則程式會造成假死的狀態,如下圖:

這裡寫圖片描述

所以說HashMap不支援多執行緒併發的操作。

Hashtable集合支援併發
示例:
只需要把HashMap示例中public static HashMap hashMap = new HashMap();修改成public static Hashtable hashtable = new Hashtable();即可。
我們就說說Hashtable不支援Iterator的編輯/刪除操作,示例如下

package com.collections.hashmapdemo;

import java.util.Hashtable;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;

public class HashtableNotModifyDemo {
    public static Hashtable hashtable = new Hashtable();
    static {
        for (int i = 0; i < 5; i++) {
            hashtable.put("String" + (i + 1), i + 1);
        }
    }

    public static void main(String[] args) {
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Iterator iterator = hashtable.keySet().iterator();
                    while (iterator.hasNext()) {
                        System.out.println(iterator.next());
                        TimeUnit.SECONDS.sleep(2);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                hashtable.put("z", "zValue");
            }
        });

        threadA.start();
        threadB.start();
    }
}

執行結果

String5
java.util.ConcurrentModificationException
at java.util.HashtableEnumerator.next(Hashtable.java:1167)atcom.collections.hashmapdemo.HashtableNotModifyDemo1.run(HashtableNotModifyDemo.java:22)
at java.lang.Thread.run(Thread.java:745)

說明多執行緒呼叫該類的iterator()方法返回Iterator物件後,在呼叫put方法會報ConcurrentModificationException異常,也就是不支援Iterator併發的編輯/刪除操作。如果想實現此功能推薦使用併發集合框架提供的ConcurrentHashMap類。

ConcurrentHashMap集合支援併發示例:

需要把HashMap示例中public static HashMap hashMap = new HashMap();修改成public
static ConcurrentHashMap concurretHashMap = new ConcurrentHashMap();即可。

ConcurrentHashMap集合支援Iterator併發操作示例:

把類HashtableNotModifyDemo中的public static Hashtable hashtable = new Hashtable();修改為public static ConcurrentHashMap concurretHashMap = new ConcurrentHashMap();測試即可。

ConcurrentSkipListMap類

這個併發集合類支援排序功能。一般此集合中存放的物件要實現Comparable介面,方便此集合類排序,如果不實現此介面,則會按照預設排序。

ConcurrentSkipListSet類

這個併發集合類不僅支援排序功能,還不允許重複的元素。一般此集合中存放的物件要實現Comparable介面,並且重寫equals和hashCode方法。

ConcurrentLinkedQueue類

這個併發集合類提供了併發環境的佇列操作。

  • poll()當沒有獲取到資料時返回null,如果有資料則一處表頭並將表頭資料返回。
  • element()當沒有獲取到資料時拋NoSuchElementException異常,如果有資料則返回表頭資料。
  • peek()當沒有獲取導資料是返回null,獲取到資料時則不移除表頭,並將表頭資料返回。

ConcurrentLinkedDeque類

ConcurrentLinkedQueue支援對佇列頭操作,而ConcurrentLinkedDeque也支援佇列頭和列尾雙向操作。

CopyOnWriteArrayList類

ArrayList是執行緒不安全的,如果想在併發中實現執行緒安全,則可以使用CopyOnWriteArrayList類,使用方法和ArrayList無異。

CopyOnWriteArraySet類

ArraySet是執行緒不安全的,如果想在併發中實現執行緒安全,則可以使用CopyOnWriteArraySet類,使用方法和ArraySet無異。

阻塞佇列

to be continue……