首頁 > Java > java教程 > 在Java高並發多執行緒環境下,如何確保ArrayList、HashSet和HashMap的執行緒安全性?

在Java高並發多執行緒環境下,如何確保ArrayList、HashSet和HashMap的執行緒安全性?

王林
發布: 2023-05-09 22:49:07
轉載
1670 人瀏覽過

1.ArrayList的執行緒不安全解決方案

將main方法的第一行註解打開,多執行幾次,會看到如下圖這樣的例外訊息:???

在Java高並發多執行緒環境下,如何確保ArrayList、HashSet和HashMap的執行緒安全性?

#這是一個並發修改異常,首先ArrayList肯定是線程不安全的,產生這個異常的原因就是可能第一個線程剛進入ArrayList 集合中要進行add 操作時,另外一個線程此時也進來進行add 操作,而第三個線程又進來進行get 操作,導致讀寫沒辦法進行同步了,最終打印結果的時候就炸了。

解決方案看程式碼中的剩下幾行註解。

package test.notsafe;
 
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
 
/**
 * 演示ArrayList的线程不安全问题及解决方案
 */
public class ThreadDemo2 {
    public static void main(String[] args) {
        //List<String> list = new ArrayList<>();
 
        //解决方法1:使用Vector
        //List<String> list = new Vector<>();
 
        //解决方法2:Collections
        //List<String> list = Collections.synchronizedList(new ArrayList<>());
 
        //解决方法3:CopyOnWriteArrayList
        List<String> list = new CopyOnWriteArrayList<>();
 
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}
登入後複製

在Java高並發多執行緒環境下,如何確保ArrayList、HashSet和HashMap的執行緒安全性?

關於CopyOnWriteArrayList 解決線程不安全問題的簡單解釋:就看源碼中的add(E e) 這個方法:

public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
登入後複製

這個CopyOnWriteArrayList 在進行add 新增操作之前,先進行lock 上鎖,然後透過getArray() 取得到原始ArrayList 集合容器,之後呼叫Arrays.copyOf 方法將原容器拷貝出一個新容器,因為要新增(長度自然也要1),之後在這個新容器中加入元素,加入完成之後,呼叫setArray 方法將原始容器的參考指向了這個新的容器。那麼這樣做的好處就是:添加元素在新容器中,原容器該是啥樣還是啥樣,其他線程要get讀取元素就還從原始容器中讀(即多個線程可以進行並發讀);而其他線程要add 添加,要等待其他線程完成之後,將原始容器的引用指向新容器就可以了。

CopyOnWrite 容器在面對讀寫的時候是兩個不同的容器,也是用到了讀寫分離的想法。

2.HashSet的線程不安全解決方案

這裡如果是 new HashSet 了話,仍然可能出現向上面 ArrayList 一樣的 並發修改異常。解決方案看程式碼中的註解。

package test.notsafe;
 
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
 
/**
 * 演示HashSet的线程不安全问题及解决方案
 */
public class ThreadDemo3 {
    public static void main(String[] args) {
        //Set<String> set = new HashSet<>();
 
        //解决方法1:Collections
        //Set<String> set = Collections.synchronizedSet(new HashSet<>());
 
        //解决方法2:CopyOnWriteArraySet
        Set<String> set = new CopyOnWriteArraySet<>();
 
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}
登入後複製

3.HashMap的執行緒不安全解決方案

package test.notsafe;
 
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
 
/**
 * 演示HashMap的线程不安全问题及解决方案
 */
public class ThreadDemo4 {
    public static void main(String[] args) {
        //Map<String,Object> map = new HashMap<>();
 
        //解决方法1:Collections
        //Map<String,Object> map = Collections.synchronizedMap(new HashMap<>());
 
        //解决方法2:ConcurrentHashMap
        Map<String,Object> map = new ConcurrentHashMap<>();
 
        for (int i = 0; i < 10; i++) {
            String key = String.valueOf(i);
            new Thread(() -> {
                map.put(key,UUID.randomUUID().toString().substring(0,8));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}
登入後複製

以上是在Java高並發多執行緒環境下,如何確保ArrayList、HashSet和HashMap的執行緒安全性?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板