線程不安全的ArrayList, Set, Map

眾所周知腹忽,ArrayList -> List -> Collection, ArrayList線程不安全财骨。同理思喊,Map, Set都是線程不安全言沐。ArrayList總結(jié)起來就是: 初始化size為10的Object類型的數(shù)組。多并發(fā)修改值時可能: ConcurrentModificationException.

出現(xiàn)ConcurrentModificationExceptionArrayList舉例:30個線程同時對list進行add:

public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }

各種exception:

  1. 同時對list讀寫: ConcurrentModificationException. 解決辦法: Vector, Collections.synchronizedList(new ArrayList<>()), CopyOnWriteArrayList
  2. IllegalMonitorStateException: 沒有加lock/synchronized: 多線程爭搶操作資源類
  3. java自帶的類.getClass().getClassLoader().**getParent()**...: NullPointerException;
  4. thread.start()調(diào)用多次: IllegalThreadStateException

ArrayList -> CopyOnWriteArrayList
HashSet  -> CopyOnWriteArraySet
   |
   v
HashMap  -> ConcurrentHashMap
  1. ArrayList: default cap: 10; grow strategy: newCap= = oldCap + (oldCap >> 1): 15, 22...
  2. HashMap: default cap: 16; grow strategy: newCap = oldCap << 1; 是2^n;

ArrayList擴容中绎秒,new/old交替賦值元素用的方法是Arrays.copyOf:

elementData = Arrays.copyOf(elementData, newCapacity);

如何用線程安全的ArrayList?

Vector. 但是浦妄,不好??尼摹,因為這是JDK 1.0的, 加了synchronized并發(fā)性下降见芹。實際上ArrayList是JDK 1.2的(Doug Lea),就是為了解決并發(fā)性能低才有的這個類.

怎么辦?
Collections是工具類:Collections.synchronizedList(new ArrayList<>()).可以點進去看源碼, 和ArrayList的add方法的區(qū)別就是, 方法前面有沒有synchronized修飾.

還有更吊的: java.util.concurrent包中的類: CopyOnWriteArrayList, 寫時復制蠢涝。也是線程安全的.

為什么? 點進去:

public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

點進去:

final void setArray(Object[] a) {
        array = a;
}

點進去:

/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;

add方法

/**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
public boolean add(E e) {
        final ReentrantLock lock = this.lock; // only 1 write/read happens at a time
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1); // add just 1 element
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

可以看出來, CopyOnWrite這種寫時復制的容器玄呛,不是直接往那個容器添加元素,而是將當前的容器elements進行copy得到一個新的容器newElements,然后往新的容器中寫和二。寫完之后徘铝,把指向原來容器的引用指向新的容器setArray(newElements), 這樣在寫的時候并不影響從之前的容器中讀, i.e. 可以對CopyOnWrite容器進行并發(fā)的讀,這個讀不需要加鎖,因為當前容器不會添加任何元素惕它。所以CopyOnWrite是一種讀寫分離的思想怕午,讀寫分離的容器。

當然淹魄,這種Arrays.copyOf過于頻繁也不好郁惜,這種就看場景甲锡,如果是讀多寫少的場景, e.g. 跟mysql那種類似的場景,用這個就很合適。

Set:

public static void main(String[] args) {
//        listNotSafe();
        Set<String> set = new HashSet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }

因為HashSet和前面的ArrayList類似,也不是線程安全的,所以上面的會有ConcurrentModificationException;
問: HashSet是怎么實現(xiàn)的?

public HashSet() {
        map = new HashMap<>();
    }

誒?那一個KV對一個是就只是單個元素四苇,怎么搞的?
找add:

public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

PRESENT是個常量:

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

噢煌集,原來value就是個new Object().

為什么value不是null? 因為既然set底層是map, 而map在remove(Object key``時返回value, 如果底層都是null`,不能區(qū)分是否是否remove成功

Map: 同:

Map<String, String> map = new HashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }

報錯: ConcurrentModificationException;
換map: ConcurrentHashMap:


碰到bug,四步走:

  1. 故障現(xiàn)象
  2. 導致原因
  3. 解決方案
  4. 優(yōu)化建議

天下武功根本沒有高低之分,只有習武之人有強弱之別

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颓屑,一起剝皮案震驚了整個濱河市器腋,隨后出現(xiàn)的幾起案子诊县,更是在濱河造成了極大的恐慌,老刑警劉巖措左,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件依痊,死亡現(xiàn)場離奇詭異,居然都是意外死亡怎披,警方通過查閱死者的電腦和手機胸嘁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凉逛,“玉大人性宏,你說我怎么就攤上這事∽捶桑” “怎么了毫胜?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長诬辈。 經(jīng)常有香客問我酵使,道長,這世上最難降的妖魔是什么自晰? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任凝化,我火速辦了婚禮稍坯,結(jié)果婚禮上酬荞,老公的妹妹穿的比我還像新娘搓劫。我一直安慰自己,他們只是感情好混巧,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布枪向。 她就那樣靜靜地躺著,像睡著了一般咧党。 火紅的嫁衣襯著肌膚如雪秘蛔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天傍衡,我揣著相機與錄音深员,去河邊找鬼。 笑死蛙埂,一個胖子當著我的面吹牛倦畅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绣的,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼叠赐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了屡江?” 一聲冷哼從身側(cè)響起芭概,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎惩嘉,沒想到半個月后罢洲,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡文黎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年奏路,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臊诊。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸽粉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抓艳,到底是詐尸還是另有隱情触机,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布玷或,位于F島的核電站儡首,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏偏友。R本人自食惡果不足惜蔬胯,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望位他。 院中可真熱鬧氛濒,春花似錦产场、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至骗奖,卻和暖如春确徙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背执桌。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工鄙皇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仰挣。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓育苟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親椎木。 傳聞我的和親對象是個殘疾皇子违柏,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容