Concurrent包詳解及使用場景


Concurrent包是jdk1.5所提供的一個針對高並發進行編程的包。

1.阻塞式隊列 - BlockingQueue

遵循先進先出(FIFO)的原則。阻塞式隊列本身使用的時候是需要指定界限的。

在生產者消費者模型中,生產數據和消費數據的速率不一致,如果生產數據速度快一些,消費(處理)不過來,就會導致數據丟失。這時候我們就可以應用上阻塞隊列來解決這個問題。

ArrayBlockingQueue - 阻塞式順序隊列

底層是基於數組來進行存儲,使用的時候需要指定一個容量,容量指定之后不可改變。--- 生產 - 消費模型

    public static void main(String[] args) throws InterruptedException {

        // 這個隊列在創建的時候需要指定容量,容量在指定之后不可變
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(5);

        // 添加隊列
        queue.add("a");
        queue.add("b");
        queue.add("c");
        queue.add("d");
        queue.add("e");

        // 如果隊列已滿,則拋出異常 - IllegalStateException
        // queue.add("a");
        // 返回值標記元素是否成功添加到隊列里面
        // 如果隊列已滿,則返回false
        // boolean b = queue.offer("b");
        // System.out.println(b);
        // 如果隊列已滿,會產生阻塞 --- 直到這個隊列中有元素被取出,才會放開阻塞
        // queue.put("c");

        // 定時阻塞
        // 在3s之內如果有元素被取出,那么元素就會添加到隊列中
        // 如果3s之后隊列依然是滿的,那么返回false表示添加失敗
        boolean b = queue.offer("d", 3000, TimeUnit.MILLISECONDS);
        System.out.println(b);
        System.out.println(queue);
    }

LinkedBlockingQueue - 阻塞式鏈式隊列

底層是基於鏈表(節點)來進行數據的存儲。在使用的時候可以指定初始容量,也可以不指定。

如果指定了容量,就以指定的容量為准來進行存儲;

如果不指定容量,那么默認容量是 Integer.MAX_VALUE -> 231 - 1。如果不指定容量,一般認為這個容量是無限的。

PriorityBlockingQueue - 具有優先級的阻塞式隊列

如果不指定容量,默認容量是11.如果將元素取出,那么會對元素進行自然排序 --- 要求存儲的對象所對應的類必須實現Comparable,重寫compareTo方法,講比較規則寫到方法中;如果進行迭代遍歷,那么不保證排序。

SynchronousQueue - 同步隊列

只允許存儲1個元素。

2.並發映射 - ConcurrentMap

HashMap - 底層依靠數組+鏈表存儲的數據

默認初始容量是16,默認加載因子是0.75f,默認擴容每次增加一倍。本身是一個異步式線程不安全的映射

Hashtable - 同步式線程安全的映射

對外提供的方法都是同步方法

ConcurrentHashMap - 異步式線程安全的映射

在jdk1.8之前,采用分段(分桶)鎖, 分段鎖采用的是讀寫鎖機制(讀鎖:允許多個線程讀,但是不允許線程寫;寫鎖:允許一個線程寫,但是不允許線程讀);jdk1.8不再采用鎖機制,而是CAS(Compare and Swap)算法, 減小了鎖的開銷;如果一個桶中的元素個數超過了8個,那么會將這個桶的鏈表扭轉成一棵紅黑樹(自平衡二叉查找樹)結構。

ConcurrentNavigableMap - 並發導航映射

本身是一個接口,所以更多的是使用實現類

ConcurrentSkipListMap - 並發跳躍表映射

跳躍表:為了提高查詢效率所產生的一種數據結構

跳躍表是典型的以空間換時間的產物。

跳躍表的時間復雜度是O(logn)

如果跳躍表中插入新的元素,新的元素是否往上提取,遵循“拋硬幣”原則 --- 1/2原則

只要保證這個節點有一半的概率被提取就可以

跳躍表適合大量查詢而不增刪的場景

CountDownLatch - 閉鎖 - 線程減鎖

對線程neg進行計數,當計數歸零的時候會開放阻塞線程繼續往下執行

public class CountDownLatchDemo {
    
    public static void main(String[] args) throws InterruptedException {
        
        CountDownLatch cdl = new CountDownLatch(5);
        
        new Thread(new Teacher(cdl)).start();
        new Thread(new Student(cdl)).start();
        new Thread(new Student(cdl)).start();
        new Thread(new Student(cdl)).start();
        new Thread(new Student(cdl)).start();
        
        // 表示讓線程阻塞,直到計數歸零的時候阻塞才能放開
        cdl.await();        
        System.out.println("考試結束~~~");      
    }
}

class Student implements Runnable {
    
    private CountDownLatch cdl;

    public Student(CountDownLatch cdl) {
        this.cdl = cdl;
    }

    @Override
    public void run() {
        
        System.out.println("學生來到考場,准備考試~~~");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("學生交卷離開考場");
        
        // 計數-1
        cdl.countDown();
    }
}

class Teacher implements Runnable {
    
    private CountDownLatch cdl;

    public Teacher(CountDownLatch cdl) {
        this.cdl = cdl;
    }

    @Override
    public void run() {

        System.out.println("老師來到考場,准備考試~~~");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("老師收卷離開了考場~~~");
        
        cdl.countDown();
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM