Java 集合線程安全的幾種解決方法
在高并發(fā)環(huán)境下,Java集合ArrayList和HashMap讀寫可能會(huì)出現(xiàn)安全問題。其中有幾個(gè)解決辦法:
- 使用Collections類方法Collections.synchronizedList和Collections.synchronizedMap
- 在Java并發(fā)包中提供了CopyOnWriteArrayList和ConcurrentHashMap類
一、ArrayList 的線程安全問題
ArrayList是 Java 中最常用的動(dòng)態(tài)數(shù)組實(shí)現(xiàn)類,它基于數(shù)組實(shí)現(xiàn),允許元素重復(fù),并且可以根據(jù)元素的添加自動(dòng)擴(kuò)容。在單線程環(huán)境下,ArrayList使用起來非常方便,但在多線程環(huán)境中,它并不具備線程安全性。
ArrayList在多線程環(huán)境下出現(xiàn)線程安全問題,主要體現(xiàn)在其add、remove等操作上。這些操作并不是原子性的,以add操作為例,在添加元素時(shí),ArrayList需要檢查數(shù)組是否已滿,如果已滿則需要進(jìn)行擴(kuò)容操作,擴(kuò)容過程涉及到創(chuàng)建新數(shù)組、復(fù)制原數(shù)組元素等步驟。在多線程環(huán)境下,當(dāng)多個(gè)線程同時(shí)執(zhí)行add操作時(shí),可能會(huì)出現(xiàn)兩個(gè)線程同時(shí)檢測(cè)到數(shù)組已滿,進(jìn)而各自進(jìn)行擴(kuò)容操作,最終導(dǎo)致數(shù)據(jù)丟失、覆蓋或者其他不可預(yù)知的錯(cuò)誤。
例如以下代碼,模擬了多線程環(huán)境下ArrayList可能出現(xiàn)的問題:
import java.util.ArrayList;
import java.util.List;
public class ArrayListThreadSafetyDemo {
private static List<Integer> list = new ArrayList<>();
public static void main(String[] args) {
Thread[] threads = new Thread[1000];
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 100; j++) {
list.add(j);
}
});
threads[i].start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("List size: " + list.size());
}
}運(yùn)行這段代碼,可能會(huì)發(fā)現(xiàn)最終輸出的list大小并不是預(yù)期的100000,這就是因?yàn)槎嗑€程操作ArrayList導(dǎo)致的線程安全問題。
為了解決ArrayList在多線程環(huán)境下的線程安全問題,可以使用Collections.synchronizedList方法將ArrayList包裝成線程安全的列表。另外,Java 并發(fā)包中還提供了CopyOnWriteArrayList,它在寫入操作(如add、remove)時(shí),會(huì)先復(fù)制原數(shù)組,在新數(shù)組上進(jìn)行操作,操作完成后再將新數(shù)組賦值給原數(shù)組引用,雖然這種方式會(huì)消耗更多的內(nèi)存,但在讀取操作頻繁的場(chǎng)景下,能有效提高并發(fā)性能且保證線程安全。
下面代碼用CopyOnWriteArrayList解決ArrayList線程安全問題
List<String> copyOnWriteList = new CopyOnWriteArrayList<>();
二、HashMap 的線程安全問題
HashMap是 Java 中常用的鍵值對(duì)存儲(chǔ)集合,它基于哈希表實(shí)現(xiàn),具有高效的查找、插入和刪除性能。但與ArrayList一樣,HashMap在多線程環(huán)境下也不是線程安全的。
HashMap在多線程環(huán)境下存在線程安全問題,主要體現(xiàn)在其哈希表的結(jié)構(gòu)在多線程操作時(shí)可能會(huì)被破壞。在 JDK 1.7 及之前的版本中,HashMap采用數(shù)組 + 鏈表的結(jié)構(gòu),當(dāng)多個(gè)線程同時(shí)進(jìn)行插入操作且發(fā)生哈希沖突時(shí),可能會(huì)導(dǎo)致鏈表形成環(huán)形結(jié)構(gòu),從而在后續(xù)的查找操作中陷入死循環(huán)。在 JDK 1.8 之后,HashMap引入了紅黑樹,雖然一定程度上改善了性能,但依然無法解決多線程操作時(shí)的數(shù)據(jù)競(jìng)爭(zhēng)問題。
下面是一個(gè)簡(jiǎn)單的示例代碼,模擬多線程環(huán)境下HashMap可能出現(xiàn)的問題:
import java.util.HashMap;
import java.util.Map;
public class HashMapThreadSafetyDemo {
private static Map<String, Integer> map = new HashMap<>();
public static void main(String[] args) {
Thread[] threads = new Thread[1000];
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 100; j++) {
map.put("key" + j, j);
}
});
threads[i].start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Map size: " + map.size());
}
}運(yùn)行上述代碼,可能會(huì)出現(xiàn)數(shù)據(jù)丟失、程序卡死等情況。
為了保證HashMap在多線程環(huán)境下的線程安全,可以使用Collections.synchronizedMap方法將HashMap包裝成線程安全的映射。此外,Java 并發(fā)包中的ConcurrentHashMap是專門為多線程環(huán)境設(shè)計(jì)的高效線程安全映射,它通過分段鎖、CAS 操作等技術(shù),允許多個(gè)線程同時(shí)訪問不同的段,大大提高了并發(fā)性能,在多線程場(chǎng)景下是HashMap的理想替代方案。
下面代碼用ConcurrentHashMap解決HashMap線程安全問題
Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
三、總結(jié)
在多線程環(huán)境下使用 Java 集合類時(shí),一定要充分考慮線程安全問題。對(duì)于ArrayList和HashMap這類非線程安全的集合,開發(fā)者可以根據(jù)具體的業(yè)務(wù)場(chǎng)景選擇合適的解決方案,如使用同步包裝類或者 Java 并發(fā)包中提供的線程安全集合類。只有正確處理集合的線程安全問題,才能確保程序在多線程環(huán)境下穩(wěn)定、高效地運(yùn)行。
到此這篇關(guān)于Java 集合線程安全的幾種解決方法的文章就介紹到這了,更多相關(guān)Java 集合線程安全內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java sftp下載文件報(bào)錯(cuò)Caused by:com.jcraft.jsch.JSchExcep
文章講述了作者在日常工作中遇到的JSch連接問題,經(jīng)過分析發(fā)現(xiàn)是由于連接泄露導(dǎo)致的,作者提出了解決方案,并給出了使用建議:1.在finally代碼塊中關(guān)閉連接;2.在真正使用階段再創(chuàng)建連接,避免創(chuàng)建后不使用又忘記關(guān)閉連接2024-11-11
Java 實(shí)用注解篇之@Qualifier 深度解析及實(shí)戰(zhàn)案例
在Spring框架中,@Qualifier是一個(gè)常見的注解,主要用于解決依賴注入(DI)時(shí)的歧義性,本文給大家介紹Java 實(shí)用注解篇之@Qualifier 深度解析及實(shí)戰(zhàn)案例,感興趣的朋友一起看看吧2025-06-06
RestTemplate設(shè)置超時(shí)時(shí)間及返回狀態(tài)碼非200處理
這篇文章主要為大家介紹了RestTemplate設(shè)置超時(shí)時(shí)間及返回狀態(tài)碼非200處理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
關(guān)于Jmeter接口測(cè)試實(shí)戰(zhàn)-Cookies
這篇文章主要介紹了關(guān)于Jmeter接口測(cè)試實(shí)戰(zhàn)-Cookies問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
SpringBoot + SpringSecurity 短信驗(yàn)證碼登錄功能實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot + SpringSecurity 短信驗(yàn)證碼登錄功能實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06
Java之通過OutputStream寫入文件與文件復(fù)制問題
這篇文章主要介紹了Java之通過OutputStream寫入文件與文件復(fù)制問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
Spring如何基于Proxy及cglib實(shí)現(xiàn)動(dòng)態(tài)代理
這篇文章主要介紹了Spring如何基于Proxy及cglib實(shí)現(xiàn)動(dòng)態(tài)代理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06

