溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java并發(fā)容器的介紹和使用

發(fā)布時(shí)間:2021-06-26 14:56:49 來源:億速云 閱讀:164 作者:chen 欄目:編程語言

這篇文章主要講解了“Java并發(fā)容器的介紹和使用”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Java并發(fā)容器的介紹和使用”吧!

簡(jiǎn)介

前面我們介紹了同步容器,它的很大一個(gè)缺點(diǎn)就是在高并發(fā)下的環(huán)境下,性能差;

針對(duì)這個(gè),于是就有了專門為高并發(fā)設(shè)計(jì)的并發(fā)容器類;

因?yàn)椴l(fā)容器類都位于java.util.concurrent下,所以我們也習(xí)慣把并發(fā)容器簡(jiǎn)稱為JUC容器;

相對(duì)應(yīng)的還有JUC原子類、JUC鎖、JUC工具類等等(這些后面再介紹)

今天就讓我們簡(jiǎn)單來了解下JUC中并發(fā)容器的相關(guān)知識(shí)點(diǎn)

文章如果有問題,歡迎大家批評(píng)指正,在此謝過啦

目錄

  1. 什么是并發(fā)容器

  2. 為什么會(huì)有并發(fā)容器

  3. 并發(fā)容器、同步容器、普通容器的區(qū)別

正文

1. 什么是并發(fā)容器

并發(fā)容器是針對(duì)高并發(fā)專門設(shè)計(jì)的一些類,用來替代性能較低的同步容器

常見的并發(fā)容器類如下所示:

Java并發(fā)容器的介紹和使用

這節(jié)我們主要以第一個(gè)ConcurrentHashMap為例子來介紹并發(fā)容器

其他的以后有空會(huì)單獨(dú)開篇分析

2. 為什么會(huì)有并發(fā)容器

其實(shí)跟同步容器的出現(xiàn)的道理是一樣的:

同步容器是為了讓我們?cè)诰帉懚嗑€程代碼時(shí),不用自己手動(dòng)去同步加鎖,為我們解放了雙手,去做更多有意義的事情(有意義?雙手?);

而并發(fā)容器則又是為了提高同步容器的性能,相當(dāng)于同步容器的升級(jí)版;

這也是為什么Java一直在被人唱衰,卻又一直沒有衰退的原因(大佬們也很焦慮?。。。。?

不過話說回來,大佬們焦慮地有點(diǎn)過頭了;不敢想Java現(xiàn)在都升到16級(jí)了,而我們始終還在8級(jí)徘徊。

3. 并發(fā)容器、同步容器、普通容器的區(qū)別

這里的普通容器,指的是沒有同步和并發(fā)的容器類,比如HashMap

三個(gè)對(duì)比著來介紹,這樣會(huì)更加清晰一點(diǎn)

下面我們分別以HashMap, HashTable, ConcurrentHashMap為例來介紹

性能分析

下面我們來分析下他們?nèi)齻€(gè)之間的性能區(qū)別:

注:這里普通容器用的是單線程來測(cè)試的,因?yàn)槎嗑€程不安全,所以我們就不考慮了

有的朋友可能會(huì)說,你這不公平啊,可是沒辦法呀,誰讓她多線程不安全呢。

如果非要讓我在安全和性能之間選一個(gè)的話,那我選 ConcurrentHashMap(我都要)

他們?nèi)齻€(gè)之間的關(guān)系,如下圖

Java并發(fā)容器的介紹和使用 (紅色表示堵的厲害,橙色表示堵的一般,綠色表示暢通)

可以看到:

  • 單線程中操作普通容器時(shí),代碼都是串行執(zhí)行的,同一時(shí)刻只能put或get一個(gè)數(shù)據(jù)到容器中

  • 多線程中操作同步容器時(shí),可以多個(gè)線程排隊(duì)去執(zhí)行,同一時(shí)刻也是只能put或get一個(gè)數(shù)據(jù)到同步容器中

  • 多線程中操作并發(fā)容器時(shí),可以多個(gè)線程同時(shí)去執(zhí)行,也就是說同一時(shí)刻可以有多個(gè)線程去put或get多個(gè)數(shù)據(jù)到并發(fā)容器中(可同時(shí)讀讀,可同時(shí)讀寫,可同時(shí)寫寫-有可能會(huì)阻塞,這里是以ConcurrentHashMap為參考)

下面我們用代碼來復(fù)現(xiàn)下上面圖中所示的效果(慢-中-快)

  1. HashMap 測(cè)試方法

public static void hashMapTest(){
  Map<String, String> map = new HashMap<>();
  long start = System.nanoTime();
	// 創(chuàng)建10萬條數(shù)據(jù) 單線程
  for (int i = 0; i < 100_000; i++) {
		// 用UUID作為key,保證key的唯一
    map.put(UUID.randomUUID().toString(), String.valueOf(i));
    map.get(UUID.randomUUID().toString());
  }
  long end = System.nanoTime();
  System.out.println("hashMap耗時(shí):");
  System.out.println(end - start);
}
  1. HashTable 測(cè)試方法

public static void hashTableTest(){
  Map<String, String> map = new Hashtable<>();
  long start = System.nanoTime();
	// 創(chuàng)建10個(gè)線程 - 多線程
  for (int i = 0; i < 10; i++) {
    new Thread(()->{
      // 每個(gè)線程創(chuàng)建1萬條數(shù)據(jù)
      for (int j = 0; j < 10000; j++) {
        // UUID保證key的唯一性
        map.put(UUID.randomUUID().toString(), String.valueOf(j));
        map.get(UUID.randomUUID().toString());
      }
    }).start();
  }
	// 這里是為了等待上面的線程執(zhí)行結(jié)束,之所以判斷>2,是因?yàn)樵贗DEA中除了main thread,還有一個(gè)monitor thread
  while (Thread.activeCount()>2){
    Thread.yield();
  }
  long end = System.nanoTime();
  System.out.println("hashTable耗時(shí):");
  System.out.println(end - start);
}
  1. concurrentHashMap 測(cè)試方法

public static void concurrentHashMapTest(){
  Map<String, String> map = new ConcurrentHashMap<>();
  long start = System.nanoTime();
  // 創(chuàng)建10個(gè)線程 - 多線程
  for (int i = 0; i < 10; i++) {
    new Thread(()->{
      // 每個(gè)線程創(chuàng)建1萬條數(shù)據(jù)
      for (int j = 0; j < 10000; j++) {
        // UUID作為key,保證唯一性
        map.put(UUID.randomUUID().toString(), String.valueOf(j));
        map.get(UUID.randomUUID().toString());
      }
    }).start();
  }
	// 這里是為了等待上面的線程執(zhí)行結(jié)束,之所以判斷>2,是因?yàn)樵贗DEA中除了main thread,還有一個(gè)monitor thread
  while (Thread.activeCount()>2){
    Thread.yield();
  }
  long end = System.nanoTime();
  System.out.println("concurrentHashMap耗時(shí):");
  System.out.println(end - start);
}
  1. main 方法分別執(zhí)行上面的三個(gè)測(cè)試

public static void main(String[] args) {
  hashMapTest();
  hashTableTest();
  while (Thread.activeCount()>2){
    Thread.yield();
  }
  concurrentHashMapTest();
}

運(yùn)行可以看到,如下結(jié)果(運(yùn)行多次,數(shù)值可能會(huì)變好,但是規(guī)律基本一致)

hashMap耗時(shí):
754699874 (慢)
hashTable耗時(shí):
609160132(中)
concurrentHashMap耗時(shí):
261617133(快)

結(jié)論就是,正常情況下的速度:普通容器 < 同步容器 < 并發(fā)容器

但是也不那么絕對(duì),因?yàn)檫@里插入的key都是唯一的,所以看起來正常一點(diǎn)

那如果我們不正常一點(diǎn)呢?比如極端到BT的那種

下面我們就不停地插入同一條數(shù)據(jù),上面的所有put/get都改為下面的代碼:

map.put("a", "a");
map.get("a");

運(yùn)行后,你會(huì)發(fā)現(xiàn),又是另外一個(gè)結(jié)論(大家感興趣的可以敲出來試試)

不過結(jié)論不結(jié)論的,意義不是很大;

鎖分析

普通容器沒鎖

同步容器中鎖的都是方法級(jí)別,也就是說鎖的是整個(gè)容器,我們先來看下HashTable的鎖

public synchronized V put(K key, V value) {}
public synchronized V remove(Object key) {}

可以看到:因?yàn)殒i是內(nèi)置鎖,住的是整個(gè)容器

所以我們?cè)?strong>put的時(shí)候,其他線程都不能put/get

而我們?cè)?strong>get的時(shí)候,其他線程也都不能put/get

所以同步容器效率會(huì)比較

并發(fā)容器,我們以1.7的ConcurrentHashMap為例來說下(之所以選1.7,是因?yàn)樗锩嫔婕暗膬?nèi)容都是前面章節(jié)介紹過的)

它的鎖粒度很小,它不會(huì)給整個(gè)容器上鎖,而是分段上鎖;

分段的依據(jù)就是key.hash,根據(jù)不同的hash值映射到不同的段(默認(rèn)16個(gè)段),然后插入數(shù)據(jù)時(shí),根據(jù)這個(gè)hash值去給對(duì)應(yīng)的段上鎖,此時(shí)其他段還是可以被其他線程讀寫的;

所以這就是文章開頭所說的,為啥ConcurrentHashMap會(huì)支持多個(gè)線程同時(shí)寫(因?yàn)橹灰迦氲膋ey的hashCode不會(huì)映射到同一個(gè)段里,那就不會(huì)沖突,此時(shí)就可以同時(shí)寫)

讀因?yàn)闆]有上鎖,所以當(dāng)然也支持同時(shí)讀

如果讀操作沒有鎖,那么它怎么保證數(shù)據(jù)的一致性呢?

答案就是以前介紹過的volatile(保證可見性、禁止重排序),它修飾在節(jié)點(diǎn)Node和值val上,保證了你get的值永遠(yuǎn)是最新的

下面是ConcurrentHashMap部分源碼,可以看到val和net節(jié)點(diǎn)都是volatile類型

static class Node<K,V> implements Map.Entry<K,V> {
  final int hash;
  final K key;
  volatile V val;
  volatile Node<K,V> next;
}

總結(jié)下來就是:并發(fā)容器ConcurrentHashMap中,多個(gè)線程可同時(shí)讀,多個(gè)線程可同時(shí)寫,多個(gè)線程同時(shí)讀和寫

總結(jié)

  1. 什么是并發(fā)容器:并發(fā)容器是針對(duì)高并發(fā)專門設(shè)計(jì)的一些類,用來替代性能較低的同步容器

  2. 為什么會(huì)有并發(fā)容器:為了提高同步容器的性能

  3. 并發(fā)容器、同步容器、普通容器的區(qū)別:

    • 性能:高 - 中 - 低

    • 鎖:粒度小 - 粒度大 - 無

    • 場(chǎng)景:高并發(fā) - 中并發(fā) - 單線程

感謝各位的閱讀,以上就是“Java并發(fā)容器的介紹和使用”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Java并發(fā)容器的介紹和使用這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI