Java多線程高并發(fā)中解決ArrayList與HashSet和HashMap不安全的方案
1.ArrayList的線程不安全解決方案
將main方法的第一行注釋打開,多執(zhí)行幾次,會(huì)看到如下圖這樣的異常信息:👇👇👇

這是一個(gè) 并發(fā)修改 異常,首先ArrayList肯定是線程不安全的,產(chǎn)生這個(gè)異常的原因就是可能第一個(gè)線程剛進(jìn)入 ArrayList 集合中要進(jìn)行 add 操作時(shí),另外一個(gè)線程此時(shí)也進(jìn)來進(jìn)行 add 操作,而第三個(gè)線程又進(jìn)來進(jìn)行 get 操作,導(dǎo)致讀寫沒辦法進(jìn)行同步了,最終打印結(jié)果的時(shí)候就炸了。
解決方案看代碼中的剩下幾行注釋。
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();
}
}
}

關(guān)于 CopyOnWriteArrayList 解決線程不安全問題的簡(jiǎn)單解釋:就看源碼中的 add(E e) 這個(gè)方法:
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();
}
}
這個(gè) CopyOnWriteArrayList 在進(jìn)行 add 添加操作之前,先進(jìn)行 lock 上鎖,然后通過 getArray() 獲取到原 ArrayList 集合容器,之后調(diào)用 Arrays.copyOf 方法將原容器拷貝出一個(gè)新容器,因?yàn)橐砑樱ㄩL(zhǎng)度自然也要 +1),之后向這個(gè)新容器中添加元素,添加完成之后,調(diào)用 setArray 方法將原容器的引用指向了這個(gè)新的容器。 那么這樣做的好處就是:添加元素在新容器中,原容器該是啥樣還是啥樣,其他線程要get讀取元素就還從原容器中讀(即多個(gè)線程可以進(jìn)行并發(fā)讀);而其他線程要 add 添加,要等待其他線程完成之后,將原容器的引用指向新容器就可以了。
CopyOnWrite 容器在面對(duì)讀和寫的時(shí)候是兩個(gè)不同的容器,也是用到了讀寫分離的思想。
2.HashSet的線程不安全解決方案
這里如果是 new HashSet 了話,仍然可能出現(xiàn)向上面 ArrayList 一樣的 并發(fā)修改異常。解決方案看代碼中的注釋。
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();
}
}
}
到此這篇關(guān)于Java多線程高并發(fā)中解決ArrayList與HashSet和HashMap不安全的方案的文章就介紹到這了,更多相關(guān)Java 多線程高并發(fā)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
自己動(dòng)手寫一個(gè)java版簡(jiǎn)單云相冊(cè)
這篇文章主要為大家分享了自己動(dòng)手寫的一個(gè)java版簡(jiǎn)單云相冊(cè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07
servlet實(shí)現(xiàn)簡(jiǎn)單的權(quán)限管理和敏感詞過濾功能
JavaEE課要求用servlet和過濾器實(shí)現(xiàn)權(quán)限管理和敏感詞過濾功能,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
Spring中@Async用法詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了Spring中@Async用法詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-02-02
詳解spring boot容器加載完后執(zhí)行特定操作
這篇文章主要介紹了詳解spring boot容器加載完后執(zhí)行特定操作,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01
Java數(shù)據(jù)結(jié)構(gòu)之LinkedList的用法詳解
鏈表(Linked?list)是一種常見的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),是一種線性表。Java的LinkedList(鏈表)?類似于?ArrayList,是一種常用的數(shù)據(jù)容器,本文就來簡(jiǎn)單講講它的使用吧2023-05-05
java 使用HttpURLConnection發(fā)送數(shù)據(jù)簡(jiǎn)單實(shí)例
這篇文章主要介紹了java 使用HttpURLConnection發(fā)送數(shù)據(jù)簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-06-06
mybatis代碼生成+自定義注解+自定義注釋實(shí)例
這篇文章主要介紹了mybatis代碼生成+自定義注解+自定義注釋實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
Springmvc中的轉(zhuǎn)發(fā)重定向和攔截器的示例
本篇文章主要介紹了Springmvc中的轉(zhuǎn)發(fā)重定向和攔截器的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
在Java 8中將List轉(zhuǎn)換為Map對(duì)象方法
這篇文章主要介紹了在Java 8中將List轉(zhuǎn)換為Map對(duì)象方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11

