久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

關(guān)于Java集合,,還停留在ArrayList、HashSet層面嗎

 貪挽懶月 2022-06-20 發(fā)布于廣東

我們平時(shí)編碼時(shí)使用集合類,,都是new 一個(gè) ArrayList 或者 HashSet 或者 HashMap就直接開用,,好像也沒遇到啥問題。但是多線程情況下就會(huì)有問題。下面一一道來,。

List

1,、故障現(xiàn)象:
先看下面一段代碼:

1 List<String> list = new ArrayList<>();
2 for (int x = 0; x < 30; x ++){
3        new Thread( () -> {
4            list.add("哈哈");
5        }).start();
6 }
7 System.out.println(list.toString());

這段代碼很簡(jiǎn)單,就是創(chuàng)建30個(gè)線程,,每個(gè)線程往list集合add元素,,看似沒啥問題,看代碼的運(yùn)行結(jié)果:

運(yùn)行結(jié)果


運(yùn)行拋異常了,,這便是并發(fā)修改異常,。

2、導(dǎo)致原因:
并發(fā)修改異常是因?yàn)榫€程并發(fā)爭(zhēng)搶修改導(dǎo)致,。舉個(gè)例子:上課的時(shí)候老師拿了一份名單要點(diǎn)名,,說來了的同學(xué)就上去簽自己的名字。這份名單就是集合,,每個(gè)同學(xué)就是一個(gè)線程,。上去簽名就是往集合中添加元素的add操作。當(dāng)張三同學(xué)上去簽名的時(shí)候,,剛寫完 “張” 字,,李四同學(xué)就上來把筆搶了去,結(jié)果就是張三同學(xué)的名只簽了一半,。這就是并發(fā)修改異常,。

3、解決方案:

  • 第一種辦法,,可以使用線程安全的Vector類,,它的方法都加了鎖,可以保證線程安全,。不過Vector現(xiàn)在很少人用,,因?yàn)椴l(fā)性不好。

  • 第二種辦法,,使用Collections工具類,。如下:

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

這個(gè)方法顧名思義,就是可以把ArrayList變成安全的,。所以它也可以解決并發(fā)修改異常,。

  • 第三種辦法,使用JUC包中的CopyOnWriteArrayList類,。CopyOnWrite的意思是寫時(shí)復(fù)制,。看看如何使用它解決并發(fā)修改異常,。

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

就是new 一個(gè) CopyOnWriteArrayList就可以了,。那么這個(gè)類為什么能保證線程安全呢,?看一下它的源碼:

 1 public boolean add(E e{
2        final ReentrantLock lock = this.lock;
3        lock.lock();
4        try {
5            Object[] elements = getArray();
6            int len = elements.length;
7            Object[] newElements = Arrays.copyOf(elements, len + 1);
8            newElements[len] = e;
9            setArray(newElements);
10            return true;
11        } finally {
12            lock.unlock();
13        }
14}

所謂寫時(shí)復(fù)制,就是寫的時(shí)候不是直接在原來的數(shù)組中寫,,而是先復(fù)制一份,,寫完后再引用這個(gè)新的。還是簽名的例子:老師說同學(xué)們一個(gè)個(gè)地上來簽名,。張三上去了,,把那份名單copy了一份,簽上了自己的名字,。在張三簽名的過程中,,其他同學(xué)還是可以讀老師的那份名單的。當(dāng)張三簽完了,,然后再告訴同學(xué)們,,之前那份名單作廢了,現(xiàn)在用這份新的,。這就是整個(gè)過程,,對(duì)應(yīng)了上面的代碼。首先用lock鎖住這段代碼,,即張三簽名過程中其他同學(xué)不能再來?yè)尮P了,;然后獲取到原來的數(shù)組,定義一個(gè)新數(shù)組,,長(zhǎng)度為原來的數(shù)組加1,,把原數(shù)組內(nèi)容復(fù)制到新數(shù)組中,這是張三復(fù)制名單的過程,;然后將要add的元素添加到新數(shù)組的最后,,這就是張三寫自己名字的過程;再后來將引用指向新數(shù)組,,這是張三告訴大家用這份新名單的過程,;最后釋放鎖,也就是張三把筆放下,,下一個(gè)同學(xué)可以去簽名了。
這也就是讀寫分離的思想,,寫的時(shí)候復(fù)制原來的,,寫操作完成前,讀數(shù)據(jù)還是讀原來的,,寫完成后,,讀新的。

Set
  • 在說Set不安全之前先簡(jiǎn)單地說一下HashSet底層是數(shù)據(jù)結(jié)構(gòu):
    HashSet底層是由HashMap實(shí)現(xiàn)的,,HashMap的key就是set集合add的元素,,而HashMap的value是一個(gè)Object類型的常量。

1、故障現(xiàn)象:

1ist<String> set = new HashSet<>();
2 for (int x = 0; x < 30; x ++){
3        new Thread( () -> {
4            set.add("哈哈");
5        }).start();
6 }
7 System.out.println(set.toString());

把上面的ArrayList換成HashSet,,一樣會(huì)報(bào)并發(fā)修改異常,。導(dǎo)致原因也是一樣的,下面直接看看解決原因,。

2,、解決方案:

  • 使用Collections工具類的synchronizedSet方法。

  • 使用CopyOnWriteArraySet類,。注意這個(gè)類,,實(shí)際上還是CopyOnWriteArrayList類??此鼧?gòu)造方法的源碼就可以知道了,。構(gòu)造方法如下:

1 public CopyOnWriteArraySet() {
2        al = new CopyOnWriteArrayList<E>();
3 }
Map

Map集合同樣會(huì)出現(xiàn)上述問題。很容易讓人想到解決方案也是和上面一樣,,其實(shí)有點(diǎn)區(qū)別,。首先,的確可以使用Collections工具類的synchronizedMap方法,,其次,,也可以使用HashTable。HashTable所有的方法都加了鎖,,所以可以保證安全,。但是也正因它所有方法都加了鎖,并發(fā)性不好,,所以不推薦使用,。第三種辦法,可能會(huì)想到寫時(shí)復(fù)制,,其實(shí)java沒有為map提供寫時(shí)復(fù)制的類,。我們可以使用ConcurrentHashMap,這個(gè)也是線程安全的,,而且性能還不錯(cuò),。它是使用了CAS來保證安全性。我另一篇文章《Java源碼解讀---HashMap&ConcurrentHashMap》中有介紹,,大家可以參考一下,。

  • Collections.synchronizedXxx原理:
    上面說到解決List、Set,、Map的安全問題都可以使用Collections工具類,,那么它原理是什么呢?來看一下源碼(拿synchronizedList來說明):

1public static <T> List<T> synchronizedList(List<T> list) {
2        return (list instanceof RandomAccess ?
3                new SynchronizedRandomAccessList<>(list) :
4                new SynchronizedList<>(list));
5}

首先它判斷你new的集合有沒有實(shí)現(xiàn)RandomAccess接口 (這個(gè)接口是一個(gè)標(biāo)記接口,,ArrayList就實(shí)現(xiàn)了這個(gè)接口,。作用就是,,如果實(shí)現(xiàn)了這個(gè)接口,那么就說明支持快速隨機(jī)訪問,,如果支持快速隨機(jī)方法,,那么取元素的時(shí)候就用for循環(huán),否則就用迭代器,。這是因?yàn)?,如果不支持快速隨機(jī)訪問,用迭代器獲取元素效率會(huì)更高,。ArrayList由數(shù)組實(shí)現(xiàn),,可以通過索引獲取元素,顯然是支持快速隨機(jī)訪問) ,。然后 new SynchronizedRandomAccessList<>(list),;其實(shí)就是對(duì)傳進(jìn)去的list的方法加上了同步代碼塊,所以可以保證線程安全,。它和Vector,、HashTable的區(qū)別也就在于,它使用的是同步代碼塊,,而后兩者使用的是同步方法,。

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多