日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【JUC并发编程05】集合的线程安全

發(fā)布時(shí)間:2025/3/20 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【JUC并发编程05】集合的线程安全 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

    • 5 集合的線程安全
      • 5.1 集合線程不安全演示
      • 5.2 解決方案-Vector
      • 5.3 解決方案-Collections
      • 5.4 解決方案-CopyOnWriteArrayList
      • 5.5 對(duì)上述三種方式的總結(jié)
      • 5.6 HashSet的線程不安全
      • 5.7 HashMap的線程不安全

5 集合的線程安全

5.1 集合線程不安全演示

在學(xué)習(xí)集合線程安全之前,先來(lái)看一下為什么在多線程中會(huì)出現(xiàn)不安全。

以 ArrayList 為例,我們進(jìn)入 ArrayList 源碼,找到 add() 方法,源碼如下

public boolean add(E e) {ensureCapacityInternal(size + 1); // Increments modCount!!elementData[size++] = e;return true; }

顯然,add() 方法沒(méi)有使用同步互斥,所以在多線程并發(fā)是,會(huì)出現(xiàn)線程異常,測(cè)試代碼如下

public class SetUnsefertyTest {public static void main(String[] args) {// 創(chuàng)建ArrayList 集合ArrayList<String> list = new ArrayList<>();// 創(chuàng)建10個(gè)線程,往 list 中添加元素for (int i = 0; i < 10; i++) {new Thread(()->{// 向集合中添加內(nèi)容list.add(UUID.randomUUID().toString().substring(0,8));// 從集合中取出內(nèi)容System.out.println(list);},String.valueOf(i)).start();}} }

會(huì)出現(xiàn)如下異常

解決該方法主要有三種,即使用這三個(gè)類:Vector、Collections、CopyOnWriteArrayList(常用)

5.2 解決方案-Vector

進(jìn)入在Vector的底層實(shí)現(xiàn),找到 add() 方法是線程安全的,源代碼如下,可以發(fā)現(xiàn) Vector 將 add() 加上同步關(guān)鍵字了

public synchronized boolean add(E e) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true; }

但是 Vector 用的不多,因?yàn)槊看螌?duì)添加的元素上鎖,而且使用的是重量級(jí)鎖synchronized是十分占用資源的,效率是十分低下的。其用法和 ArrayList 一樣。

5.3 解決方案-Collections

進(jìn)入 Collections 的底層,找到 synchronizedList(List list) 方法,源代碼如下,synchronizedList(List list) 方法返回指定列表支持的同步(線程安全的)列表

public static <T> List<T> synchronizedList(List<T> list) {return (list instanceof RandomAccess ?new SynchronizedRandomAccessList<>(list) :new SynchronizedList<>(list)); }static <T> List<T> synchronizedList(List<T> list, Object mutex) {return (list instanceof RandomAccess ?new SynchronizedRandomAccessList<>(list, mutex) :new SynchronizedList<>(list, mutex)); }

對(duì) Collections 的使用如下

List<String> list = Collections.synchronizedList(new ArrayList<>());

5.4 解決方案-CopyOnWriteArrayList

這種方法用的最多。

CopyOnWriteArrayList涉及的底層原理為 寫(xiě)時(shí)復(fù)制技術(shù)

  • 讀的時(shí)候并發(fā)(多個(gè)線程操作)
  • 寫(xiě)的時(shí)候獨(dú)立,先復(fù)制相同的空間到某個(gè)區(qū)域,將其寫(xiě)到新區(qū)域,舊新合并,并且讀新區(qū)域(每次加新內(nèi)容都寫(xiě)到新區(qū)域,覆蓋合并之前舊區(qū)域,讀取新區(qū)域添加的內(nèi)容)

進(jìn)入 CopyOnWriteArrayList 底層,來(lái)看一下它是怎么實(shí)現(xiàn)的,其 add() 源代碼如下

public boolean add(E e) {// 聲明一個(gè)重進(jìn)入鎖final ReentrantLock lock = this.lock;// 上鎖lock.lock();try {// 獲取原來(lái)的列表Object[] elements = getArray();// 獲取原來(lái)列表的長(zhǎng)度int len = elements.length;// 復(fù)制一個(gè)與原來(lái)的列表一樣的列表Object[] newElements = Arrays.copyOf(elements, len + 1);// 將新加入的元素放到列表末尾newElements[len] = e;// 舊新合并setArray(newElements);return true;} finally {// 解鎖lock.unlock();} }

對(duì) CopyOnWriteArrayList 的使用如下

List<String> list = new CopyOnWriteArrayList<>();

5.5 對(duì)上述三種方式的總結(jié)

對(duì)比三者來(lái)看,Vector和Collections雖然也可以實(shí)現(xiàn)同步,但由于這兩種方法在底層都使用了synchronized重量級(jí)鎖,使其效率很低,所以對(duì) ArrayList 的同步主要采用 CopyOnWriteArrayList

5.6 HashSet的線程不安全

HashSet 同時(shí)讀寫(xiě)時(shí)也會(huì)出現(xiàn) ConcurrentModificationException 異常

進(jìn)入 HashSet 底層,來(lái)看一下它是怎么實(shí)現(xiàn)的,其 add() 源代碼如下

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

顯然,他的問(wèn)題和 ArrayList 一樣,沒(méi)有對(duì) add(E e) 方法做同步處理

其解決方法與 CopyOnWriteArrayList 類似,在 JDK1.8 中,也有一個(gè)類叫做 CopyOnWriteArraySet ,其底層代碼如下

public boolean add(E e) {// private final CopyOnWriteArrayList<E> al;return al.addIfAbsent(e); }

通過(guò) Debug 找到了對(duì)關(guān)鍵的一個(gè)函數(shù),發(fā)現(xiàn)其實(shí)現(xiàn)方式與 CopyOnWriteArrayList 底層實(shí)現(xiàn)方式類似

// e 表示添加的元素 // snapshot 表示被復(fù)制的列表 private boolean addIfAbsent(E e, Object[] snapshot) {final ReentrantLock lock = this.lock;lock.lock();try {// 獲取當(dāng)前列表和列表長(zhǎng)度Object[] current = getArray();int len = current.length;// 如果現(xiàn)在的列表和之前的列表不同,其實(shí)顯然是不同的if (snapshot != current) {// Optimize for lost race to another addXXX operation// 取較小的長(zhǎng)度列表int common = Math.min(snapshot.length, len);for (int i = 0; i < common; i++)if (current[i] != snapshot[i] && eq(e, current[i]))return false;if (indexOf(e, current, common, len) >= 0)return false;}// 后面就是將其寫(xiě)到新區(qū)域,舊新合并Object[] newElements = Arrays.copyOf(current, len + 1);newElements[len] = e;setArray(newElements);return true;} finally {lock.unlock();} }

對(duì) CopyOnWriteArraySet 的使用如下

CopyOnWriteArraySet<String> list = new CopyOnWriteArraySet<>();

5.7 HashMap的線程不安全

HashMap 同時(shí)讀寫(xiě)時(shí)一樣會(huì)出現(xiàn) ConcurrentModificationException 異常

進(jìn)入 HashMap 底層,來(lái)看一下它是怎么實(shí)現(xiàn)的,其 put(K key, V value) 源代碼如下

public V put(K key, V value) {return putVal(hash(key), key, value, false, true); }

在 JDK1.8 中,也有一個(gè)類叫做 ConcurrentHashMap ,實(shí)現(xiàn) HashMap 的同步問(wèn)題,其底層代碼如下

final V putVal(K key, V value, boolean onlyIfAbsent) {if (key == null || value == null) throw new NullPointerException();int hash = spread(key.hashCode());int binCount = 0;for (Node<K,V>[] tab = table;;) {Node<K,V> f; int n, i, fh;// 若為空,就初始化if (tab == null || (n = tab.length) == 0)tab = initTable();// 計(jì)算hash值,得到下標(biāo)else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {// 傳入?yún)?shù)if (casTabAt(tab, i, null,new Node<K,V>(hash, key, value, null)))break; // no lock when adding to empty bin}// 當(dāng)有兩個(gè)及以上的線程正在擴(kuò)容else if ((fh = f.hash) == MOVED)tab = helpTransfer(tab, f);else {V oldVal = null;// 在這里加上了鎖synchronized (f) {if (tabAt(tab, i) == f) {if (fh >= 0) {binCount = 1;for (Node<K,V> e = f;; ++binCount) {K ek;if (e.hash == hash &&((ek = e.key) == key ||(ek != null && key.equals(ek)))) {oldVal = e.val;if (!onlyIfAbsent)e.val = value;break;}Node<K,V> pred = e;if ((e = e.next) == null) {pred.next = new Node<K,V>(hash, key,value, null);break;}}}else if (f instanceof TreeBin) {Node<K,V> p;binCount = 2;if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,value)) != null) {oldVal = p.val;if (!onlyIfAbsent)p.val = value;}}}}if (binCount != 0) {if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i);if (oldVal != null)return oldVal;break;}}}addCount(1L, binCount);return null; }

對(duì) ConcurrentHashMap 的使用如下

ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

總結(jié)

以上是生活随笔為你收集整理的【JUC并发编程05】集合的线程安全的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美丰满熟妇bbbbbb百度 | 日本精品在线观看 | 成人免费版 | 亚洲精品亚洲人成人网 | 免费啪视频 | 夜夜骚视频 | 欧美日韩啪啪 | 91看片在线看 | 久久精品91| 国产真实乱人偷精品 | 九九九久久久 | 一区二区三区四区免费视频 | 蜜桃色av| 越南少妇做受xxx片 亚洲av综合色区无码一二三区 | 99国产精品久久久久99打野战 | 在线网站av| 在线免费看污视频 | 免费小视频在线观看 | 成人免费播放视频 | 日本国产在线播放 | 又黄又色| 欧美精品网址 | 成人av片在线观看 | 欧美自拍偷拍一区二区 | 国产免费黄色录像 | 99在线小视频 | 蜜桃久久精品 | 中文一区二区在线观看 | 美女主播福利视频 | 少妇荡乳情欲办公室456视频 | 在线观看的网站 | 国产成人麻豆免费观看 | 91免费视频免费版 | 欧美色图一区二区三区 | 日韩国产精品一区二区 | 日本泡妞xxxx免费视频软件 | 亚洲一级无毛 | 91黄视频在线观看 | 天天操欧美 | 欧美日韩一区二区三区在线视频 | 日日免费视频 | 少妇4p| 五月在线视频 | 中文字幕自拍 | 亚洲三级黄色片 | 深爱激情久久 | 亚洲三级在线免费观看 | 精品久久久中文字幕 | 伊人久久视频 | 高清一区二区三区视频 | 日本中文字幕高清 | 色偷偷av男人的天堂 | 日本一卡二卡在线 | 成年人网站免费看 | 色无极在线 | 乱人伦xxxx国语对白 | 国产一区二区三区在线看 | 久久久久久久久久一级 | 黄色片a级 | 嫩草嫩草嫩草嫩草嫩草 | 国产精品毛片久久 | 亚洲精品国产精品乱码 | 一起操17c| 最近免费中文字幕 | 麻豆影音 | 性视屏| 性色影院 | 中文字幕23页 | 色婷婷久久综合中文久久蜜桃av | 丁香婷婷亚洲 | 国产h在线观看 | 黄黄的视频在线观看 | 国产黄色一级片 | 黄色网址最新 | 国产精品亚洲自拍 | 人人爽久久涩噜噜噜网站 | 正在播放木下凛凛xv99 | 国产又黄又大又粗的视频 | 直接看的毛片 | 日本丰满少妇一区二区三区 | www伊人网| 最好看的2018中文2019 | 美女啪啪网| 99热这里只有精品首页 | 视频二区三区 | 久热中文字幕 | 国产精品美女久久久久 | 一区二区三区亚洲视频 | 天堂а√在线中文在线鲁大师 | 91瑟瑟| 色播五月婷婷 | 亚洲精品久久久久久无码色欲四季 | 国产精品男同 | 五月婷婷婷 | 日本黄色成人 | 内射合集对白在线 | 国产日韩欧美精品一区二区 | 亚洲成人av综合 | 久久综合99 |