??博客主頁:?【小扳_-CSDN博客】
?感謝大家點贊??收藏?評論??
文章目錄
? ? ? ? 1.0 線程安全的集合類
? ? ? ? 1.2?線程安全的集合類 - Vector
? ? ? ? 1.3 線程安全的集合類 - Stack
? ? ? ? 1.4 線程安全的集合類 - HashTable
? ? ? ? 2.0 多線程環(huán)境使用 ArrayList
? ? ? ? 2.1 對 ArrayList 關(guān)鍵方法手動加鎖
? ? ? ? 2.2 利用 Collections.synchronized(new ArrayList) 方法創(chuàng)建
? ? ? ? 2.3 使用 CopyOnWriteArrayList 容器
? ? ? ? 3.0 多線程環(huán)境使用隊列
? ? ? ? 4.0 多線程環(huán)境使用哈希表
????????4.1 HashTable 類
? ? ? ? 4.2 ConcurrentHashMap 類
? ? ? ? 1.0 線程安全的集合類
? ? ? ? 對于原來熟悉的集合類,大部分都是不是線程安全的。
? ? ? ? 1.2?線程安全的集合類 - Vector
????????實現(xiàn)了動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu),可以根據(jù)需要動態(tài)增長或縮小。
? ? ? ? Vector 是一個線程安全的集合類主要來自以下幾個方面:
? ? ? ? 1)同步方法:Vector 中的所有方法都使用了 synchronized 關(guān)鍵字進(jìn)行同步,確保在多線程環(huán)境下對集合的操作是線程安全的。這意味著在對 Vector 進(jìn)行增刪改查等操作時,會對整個集合對象進(jìn)行加鎖,從而保證同一時刻只有一個線程能夠訪問集合。
? ? ? ? 2)迭代器同步:Vector 的迭代器是通過同步方法 synchronized 來實現(xiàn)的,這樣在迭代過程中,其他線程無法修改集合,避免了并發(fā)修改異常。
? ? ? ? 1.3 線程安全的集合類 - Stack
????????是 Java 中表示堆棧(棧)數(shù)據(jù)結(jié)構(gòu)的類,它繼承自 Vector 類,因此也是線程安全的集合類。所有方法都使用了 synchronized 關(guān)鍵字進(jìn)行同步,確保在多線程環(huán)境下對棧的操作是線程安全的。
? ? ? ? 1.4 線程安全的集合類 - HashTable
????????HashTable 是基于哈希表的數(shù)據(jù)結(jié)構(gòu),使用鍵值對存儲數(shù)據(jù),類似于 HashMap。鍵和值都不允許為 null ,并且 HashTable 的鍵值對是無序的。
????????是一個線程安全的集合類,所有對 HashTable 的操作都是同步的,即線程安全的。在每個公共方法中都使用了 synchronized 關(guān)鍵字,確保多線程環(huán)境下的并發(fā)訪問是安全的。
? ? ? ? 2.0 多線程環(huán)境使用 ArrayList
? ? ? ? 2.1 對 ArrayList 關(guān)鍵方法手動加鎖
????????自己使用同步機(jī)制,自己手動對關(guān)鍵操作方法加上鎖。如 synchronized 或者 ReentrantLock 。
? ? ? ? 2.2 利用 Collections.synchronized(new ArrayList) 方法創(chuàng)建
創(chuàng)建方式:
List<String> synchronizedList = Collections.synchronizedList(normalList);
代碼如下:
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class SynchronizedListExample { public static void main(String[] args) { // 創(chuàng)建一個普通的 ArrayList List<String> normalList = new ArrayList<>(); // 使用 Collections.synchronizedList 方法創(chuàng)建一個線程安全的 List List<String> synchronizedList = Collections.synchronizedList(normalList); // 在多線程環(huán)境下操作 synchronizedList 是線程安全的 // 可以對 synchronizedList 進(jìn)行添加、刪除、遍歷等操作而不用擔(dān)心線程安全性 // 示例:在多線程環(huán)境下添加元素 Runnable addTask = () -> { synchronizedList.add("Element"); System.out.println("Added element by thread: " + Thread.currentThread().getName()); }; // 創(chuàng)建多個線程來添加元素 Thread thread1 = new Thread(addTask); Thread thread2 = new Thread(addTask); thread1.start(); thread2.start(); // 等待線程執(zhí)行完成 try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } // 打印最終的 synchronizedList System.out.println("Final synchronizedList: " + synchronizedList); } }
? ? ? ? 2.3 使用 CopyOnWriteArrayList 容器
? ? ? ? CopyOnWriteArrayList 容器即寫時復(fù)制的容器。
? ? ? ? 當(dāng)我們往一個容器中添加元素時,不會直接往當(dāng)前容器中添加,而是先將當(dāng)前容器進(jìn)行 Copy,復(fù)制出一個新的容器,然后往新的容器里添加元素。
? ? ? ? 添加完畢元素之后,再將原容器的引用指向新的容器。
優(yōu)點:
? ? ? ? 1)這樣做的好處是我們對 CopyOnWrite 容器進(jìn)行并發(fā)的讀,而不需要加鎖,因為當(dāng)前的容器不會添加任何元素。所以 CopyOnWrite 容器也是一種讀寫分離的思想,讀和寫不同的容器。
? ? ? ? 2)在讀多寫少的場景下,性能很好,不需要加鎖競爭。
缺點:
? ? ? ? 1)占用內(nèi)存較多。
? ? ? ? 2)新寫的數(shù)據(jù)不能被第一時間讀取到。
代碼如下:
import java.util.concurrent.CopyOnWriteArrayList; public class CopyOnWriteArrayListExample { public static void main(String[] args) { CopyOnWriteArrayList<String> copyOnWriteList = new CopyOnWriteArrayList<>(); // 添加元素 copyOnWriteList.add("Element 1"); copyOnWriteList.add("Element 2"); // 創(chuàng)建多個線程來并發(fā)地修改列表 Runnable addTask = () -> { copyOnWriteList.add("New Element"); System.out.println("Added element by thread: " + Thread.currentThread().getName()); }; Thread thread1 = new Thread(addTask); Thread thread2 = new Thread(addTask); thread1.start(); thread2.start(); // 等待線程執(zhí)行完成 try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } // 打印最終的 copyOnWriteList System.out.println("Final copyOnWriteList: " + copyOnWriteList); } }
????????是一種特定的并發(fā)控制技術(shù),它在讀操作時不加鎖,只有在寫操作時才會加鎖。
? ? ? ? 3.0 多線程環(huán)境使用隊列
? ? ? ? 1)基于數(shù)值實現(xiàn)的阻塞隊列:ArrayBlockingQueue
? ? ? ? 2)基于鏈表實現(xiàn)的阻塞隊列:LinkedBlockingQueue
? ? ? ? 3)基于優(yōu)先級隊列實現(xiàn)的阻塞隊列:PriorityBlockingQueue
? ? ? ? 4)最多只包含一個元素的阻塞隊列:TransferQueue
以 LinkedBlockingQueue 舉個例子:
import java.util.concurrent.LinkedBlockingQueue; public class LinkedBlockingQueueExample { public static void main(String[] args) { LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(); // 創(chuàng)建生產(chǎn)者線程 Runnable producer = () -> { try { for (int i = 0; i < 5; i++) { String element = "Element " + i; queue.put(element); System.out.println("Produced: " + element); Thread.sleep(1000); // 模擬生產(chǎn)過程 } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }; // 創(chuàng)建消費(fèi)者線程 Runnable consumer = () -> { try { while (true) { String element = queue.take(); System.out.println("Consumed: " + element); Thread.sleep(1500); // 模擬消費(fèi)過程 } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }; // 啟動生產(chǎn)者和消費(fèi)者線程 Thread producerThread = new Thread(producer); Thread consumerThread = new Thread(consumer); producerThread.start(); consumerThread.start(); } }
????????LinkedBlockingQueue 實例 queue,然后分別創(chuàng)建了生產(chǎn)者線程和消費(fèi)者線程。生產(chǎn)者線程不斷向隊列中生產(chǎn)元素,消費(fèi)者線程則從隊列中消費(fèi)元素,實現(xiàn)了生產(chǎn)者和消費(fèi)者之間的協(xié)作。
? ? ? ? 4.0 多線程環(huán)境使用哈希表
? ? ? ? HashMap 本身不是線程安全的。
? ? ? ? 在多線程環(huán)境下使用哈希表可用使用:HashTable 、ConcurrentHashMap
????????4.1 HashTable 類
? ? ? ? 現(xiàn)在雖然已經(jīng)有了 HashTable 類實現(xiàn)了線程安全了,只是簡單的把關(guān)鍵方法加上了 synchronized 關(guān)鍵字而已。一個 HashTable 只有一把鎖,兩個線程訪問 HashTable 中的任意數(shù)據(jù)都會出現(xiàn)鎖競爭。
? ? ? ? 1)如果多線程訪問同一個 HashTable 就會直接造成鎖沖突。
? ? ? ? 2)size 屬性也是通過 synchronized 來控制同步,也是比較慢的。
? ? ? ? 3)一旦觸發(fā)擴(kuò)容,就由該線程完成整個擴(kuò)容過程,這個過程會涉及到大量的元素拷貝,效率會非常低。
? ? ? ? 4.2 ConcurrentHashMap 類
? ? ? ? 相比于 HashTable 做出了一系列的改進(jìn)和優(yōu)化。
? ? ? ? ConcurrentHashMap 每個哈希桶都有一把鎖,只有兩個線程訪問的恰好是同一個哈希桶上的數(shù)據(jù)才出現(xiàn)鎖沖突。但是這個概率很低,所以鎖沖突也很小,鎖競爭小。
? ? ? ? 1)讀操作沒有加鎖,但是使用了 Volatile 保證從內(nèi)存讀取結(jié)果。只對寫操作進(jìn)行加鎖,加鎖的方式仍然是 synchronized ,但是不是鎖整個對象,而是“鎖桶”,每一個鏈表的頭節(jié)點作為鎖對象,大大降低了鎖沖突的概率。由于哈希表是由數(shù)組與鏈表或者紅黑樹組成的,數(shù)組的長度很長,因此相對鏈表的長度來說,鏈表的長度就很短了,所以在多線程中,對數(shù)組中的某一個鏈表大概率是不會沖突的,因此即使每一個鏈表都上鎖了,這個鎖也大概率是偏向鎖,大概率是沒有加鎖和解鎖的開銷。
? ? ? ? 2)充分利用 CAS 特性,比如 size 屬性通過 CAS 來更新,避免出現(xiàn)重量級鎖的情況。
? ? ? ? 3)優(yōu)化了擴(kuò)容方式:化整為零
? ? ? ? 發(fā)現(xiàn)需要擴(kuò)容的線程,只需要創(chuàng)建一個新的數(shù)組,同時只搬幾個元素過去。擴(kuò)容期間,新老數(shù)組同時存在。后續(xù)每一個來操作 ConcurrentHashMap 的線程,都會參與搬家的過程,每個操作負(fù)責(zé)搬運(yùn)一小部分元素。這個期間,插入只往新數(shù)組加,查詢需要同時新舊數(shù)組。
? ? ? ? 搬完最后一個元素再把舊數(shù)組刪掉。文章來源:http://www.zghlxwxcb.cn/news/detail-857606.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-857606.html
到了這里,關(guān)于JavaEE 初階篇-線程安全的集合類、多線程環(huán)境使用 ArrayList、隊列、哈希表(HashMap 、ConCurrentHashMap 、HashTable 的區(qū)別)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!