溫馨提示×

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

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

java中并發(fā)容器J.U.C怎么用

發(fā)布時(shí)間:2021-10-19 15:52:31 來(lái)源:億速云 閱讀:119 作者:柒染 欄目:大數(shù)據(jù)

這篇文章將為大家詳細(xì)講解有關(guān)java中并發(fā)容器J.U.C怎么用,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

并發(fā)容器是JDK提供的一個(gè)包名:java.util.concurrent

ArrayList -> CopyOnWriteArrayList

CopyOnWriteArrayList是線程安全的,寫(xiě)操作時(shí)復(fù)制,當(dāng)有新元素添加到CopyOnWriteArrayList時(shí)先從原有的list中拷貝出來(lái),然后在新的list上寫(xiě)操作,寫(xiě)完之后將原來(lái)的list指向新的list,整個(gè)操作都是在鎖的保護(hù)下進(jìn)行的,這樣做為了防止多線程下多個(gè)add操作時(shí)產(chǎn)生多個(gè)副本,導(dǎo)致最終的數(shù)據(jù)不是我們期望的。

CopyOnWriteArrayList有幾個(gè)缺點(diǎn):

  1. 由于寫(xiě)操作時(shí)需要拷貝數(shù)組,因此比較消耗內(nèi)存。當(dāng)元素內(nèi)容比較多時(shí)會(huì)導(dǎo)致Full GC

  2. 不能用于實(shí)時(shí)讀的場(chǎng)景,拷貝數(shù)組需要時(shí)間,所以調(diào)用一個(gè)set操作后,讀取到的數(shù)據(jù)還可能是舊的,雖然能做到最終一致性,但是無(wú)法滿足實(shí)時(shí)性要求。因此CopyOnWriteArrayList更適合讀多寫(xiě)少的場(chǎng)景。如果不清楚add或者set多少次操作,這個(gè)CopyOnWriteArrayList最好慎用。

HashSet、TreeSet->CopyOnWriteArraySet、ConcurrentSkipListSet

CopyOnWriteArraySet同樣也是線程安全的,底層實(shí)現(xiàn)是CopyOnWriteArrayList,因此CopyOnWriteArraySet適合大小比較小的set集合只讀操作大于寫(xiě)操作,因?yàn)樾枰獜?fù)制基礎(chǔ)數(shù)組,所以對(duì)于可變的操作(add set)的開(kāi)銷大。使用迭代器的迭代速度很快,而且不會(huì)有線程安全問(wèn)題。

ConcurrentSkipListSet與TreeSet用一樣,是支持自然排序的,可以在構(gòu)造時(shí)自定義比較器。在多線程情況下ConcurrentSkipListSet里面的contains()、add()、remove()是線程安全的,多個(gè)線程可以并發(fā)的執(zhí)行插入移除和訪問(wèn)操作,但是對(duì)于批量操作例如addAll(),removeAll(),retainAll()、containsAll()并不能保證以原子方式執(zhí)行,這些操作可以被其他線程打斷,需要額外增加鎖才行,因?yàn)樗麄儗?shí)現(xiàn)方式是分別調(diào)用contains()、add()、remove()的。因?yàn)椴l(fā)容器只能保證每一次的contains()、add()、remove()操作時(shí)原子性的,而不能保證每一次批量操作都不會(huì)被其他線程打斷。也就是多個(gè)add,多個(gè)remove操作時(shí)有其他線程進(jìn)來(lái)。

HashMap、TreeMap -> ConcurrentHashMap、ConcurrentSkipListMap

ConcurrentHashMap不允許null,在實(shí)際的應(yīng)用中除了少數(shù)的插入操作和刪除操作外,絕大部分我們使用map都是使用讀取操作,而且讀操作大多數(shù)都是成功的,基于這個(gè)前提,ConcurrentHashMap針對(duì)讀操作做了大量的優(yōu)化,因此這個(gè)類具有很高的并發(fā)性,高并發(fā)場(chǎng)景下有很好的表現(xiàn)。

ConcurrentSkipListMap是TreeMap的線程安全版本。內(nèi)部是使用skipList跳表的結(jié)構(gòu)實(shí)現(xiàn)的。ConcurrentHashMap的存取速度是ConcurrentSkipListMap的4倍左右,但是ConcurrentSkipListMap的key是有序的而ConcurrentHashMap是做不到的,ConcurrentSkipListMap支持更高的并發(fā),ConcurrentSkipListMap的存取時(shí)間是與線程數(shù)無(wú)關(guān)的,在數(shù)據(jù)量一定的情況下并發(fā)線程數(shù)越多ConcurrentSkipListMap越能體現(xiàn)出優(yōu)勢(shì)。

在較低并發(fā)情況下,可以使用Collections.synchronizedSortedMap()來(lái)實(shí)現(xiàn),也可以提供較好的效率。在高并發(fā)的情況下可以使用ConcurrentSkipListMap提供更高的并發(fā)度。要對(duì)鍵值對(duì)進(jìn)行排序時(shí)可以使用ConcurrentSkipListMap。

@Slf4j
@ThreadSafe
public class ConcurrentHashMapExample {
    // 請(qǐng)求總數(shù)
    public static int clientTotal = 5000;

    // 同時(shí)并發(fā)執(zhí)行的線程數(shù)
    public static int threadTotal = 200;

    private static Map<Integer, Integer> map = new ConcurrentHashMap<>();

    public static void main(String[] args) throws InterruptedException {
        //線程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //定義信號(hào)量
        final Semaphore semaphore = new Semaphore(threadTotal);
        //定義計(jì)數(shù)器
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for(int i = 0; i < clientTotal; i++) {
            final int count  = i;
            executorService.execute(() ->{
                try {
                    semaphore.acquire();
                    update(count);
                    semaphore.release();
                } catch (InterruptedException e) {

                    log.error("exception", e);
                }
                countDownLatch.countDown();

            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("size:{}",map.size()) ;
    }

    public static void update(int i) {
        map.put(i,i);
    }

}

輸出結(jié)果正確。

 concurrentSkipListMap:

private static Map<Integer, Integer> map = new ConcurrentSkipListMap<>();

J.U.C

java中并發(fā)容器J.U.C怎么用

安全共享對(duì)象策略 - 總結(jié)

  • 線程限制:一個(gè)被線程限制的對(duì)象,由線程獨(dú)占,并且只能被占有它的線程修改。

  • 共享只讀:一個(gè)共享只讀的對(duì)象,在沒(méi)有額外同步的情況下,可以被多個(gè)線程并發(fā)訪問(wèn),但是任何線程都不能修改它。

  • 線程安全對(duì)象:一個(gè)線程安全的對(duì)象或者容器,在內(nèi)部通過(guò)同步機(jī)制來(lái)保證線程安全,所以其他線程無(wú)需額外的同步就可以通過(guò)公共接口隨意訪問(wèn)它。

  • 被守護(hù)對(duì)象:被守護(hù)對(duì)象只能通過(guò)獲取特定的鎖來(lái)訪問(wèn)。

關(guān)于java中并發(fā)容器J.U.C怎么用就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

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

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

AI