欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java current并發(fā)包超詳細(xì)分析

 更新時(shí)間:2023年02月21日 10:13:33   作者:綠仔牛奶_  
current并發(fā)包、在JDK1.5之前Java并沒(méi)有提供線程安全的一些工具類去操作多線程,需要開發(fā)人員自行編寫實(shí)現(xiàn)線程安全,但仍然無(wú)法完全避免低性能、死鎖、資源管理等問(wèn)題。在JDK1.5時(shí)新增了java.util.current并發(fā)包,其中提供了許多供我們使用的并發(fā)編程工具類

并發(fā)包

current并發(fā)包、在JDK1.5之前Java并沒(méi)有提供線程安全的一些工具類去操作多線程,需要開發(fā)人員自行編寫實(shí)現(xiàn)線程安全,但仍然無(wú)法完全避免低性能、死鎖、資源管理等問(wèn)題。在JDK1.5時(shí)新增了java.util.current并發(fā)包,其中提供了許多供我們使用的并發(fā)編程工具類。本文對(duì)于典型的并發(fā)包做出講解

ConcurrentHashMap

Java集合框架提供了存儲(chǔ)容器HashMap用于存儲(chǔ)鍵值對(duì),但是HashMap是線程不安全的。在并發(fā)編程中,我們向HashMap添加大量數(shù)據(jù)時(shí),可能會(huì)出現(xiàn)各種預(yù)料之外的問(wèn)題。

同時(shí)Java也提供了線程安全的集合類HashTable,打開HashTable的底層我們會(huì)發(fā)現(xiàn)HashTable的所有方法都利用synchtonized進(jìn)行了上鎖機(jī)制來(lái)保證了線程安全,但是利用這種阻塞同步的機(jī)制來(lái)保證線程安全的同時(shí)會(huì)大大降低程序的性能和執(zhí)行效率,這也是為什么HashTable被淘汰的原因

在JDK1.5之后Java就提供了保證性能高效、線程安全的鍵值對(duì)存儲(chǔ)容器ConcurrentHashMap

下面我們看下HashMap、HashTable、ConcurrentHashMap的對(duì)比

public class Demo01 {
    //public static Map<String,String> maps = new HashMap<String, String>();
    //public static Map<String,String> maps = new Hashtable<String, String>();
    public static Map<String,String> maps = new ConcurrentHashMap<String, String>();
    public static void main(String[] args) throws Exception {
        Runnable task = new Temp();
        Thread t1 = new Thread(task,"A線程");
        Thread t2 = new Thread(task,"B線程");
        t1.start();
        t2.start();
        // 保證t1和t2先執(zhí)行完
        t1.join();
        t2.join();
        System.out.println("最終集合長(zhǎng)度:"+maps.size());
    }
}
class Temp implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 500000; i++) {
 Demo01.maps.put(Thread.currentThread().getName()+i,Thread.currentThread().getName()+i);
        }
    }
}

如上述代碼所示,我們啟動(dòng)兩條線程執(zhí)行同一任務(wù):向容器中添加50萬(wàn)條數(shù)據(jù),預(yù)期最終容器中的數(shù)據(jù)將會(huì)達(dá)到100萬(wàn)條。

利用HashMap存儲(chǔ)時(shí),發(fā)現(xiàn)程序會(huì)出現(xiàn)各種各樣的異常狀況

程序卡頓,不報(bào)異常也不停止

報(bào)異常

java.lang.ClassCastException: java.util.HashMap$Node cannot be cast to java.util.HashMap$TreeNode

最終產(chǎn)生錯(cuò)誤數(shù)據(jù)

利用HashTable存儲(chǔ)時(shí),發(fā)現(xiàn)HashTable可以準(zhǔn)確存儲(chǔ)。并且對(duì)比HashTable和ConcurrentHashMap兩者的存儲(chǔ)速度,發(fā)現(xiàn)大差小不差甚至HashTable還要更快。那么為什么還要說(shuō)HashTable效率低下呢?

是因?yàn)槲覀冎皇菧y(cè)試了對(duì)數(shù)據(jù)進(jìn)行的寫操作,而沒(méi)有測(cè)試其他的像查詢、修改等操作。綜合來(lái)講ConcurrentHashMap的各項(xiàng)性能優(yōu)于HashTbale,所以我們?cè)谛枰紤]線程安全時(shí),就可以采用ConcurrentHashMap進(jìn)行存儲(chǔ)數(shù)據(jù)

那么ConcurrentHashMap是如何既保證線程安全又不失高性能的存儲(chǔ)數(shù)據(jù)呢?

首先明確它的底層實(shí)現(xiàn)機(jī)制是用CAS機(jī)制+synchronized分段式鎖,屬于是悲觀和樂(lè)觀相結(jié)合

HashTable工作時(shí)會(huì)將整個(gè)哈希表進(jìn)行上鎖,此時(shí)所有其他線程都將被阻塞,效率低下

ConcurrentHashMap工作時(shí)利用synchronized進(jìn)行分段式上鎖,我們知道哈希表底層基于數(shù)組實(shí)現(xiàn),數(shù)組中每個(gè)位置形成槽位以便后續(xù)成鏈或者轉(zhuǎn)換樹結(jié)構(gòu)。而分段式上鎖就是將當(dāng)前線程所存儲(chǔ)的該位置進(jìn)行上鎖,其他位置仍可以被其他線程進(jìn)行操作。

CountDownLatch倒計(jì)數(shù)觸發(fā)

CountDownLatch同樣是current包下的一個(gè)同步工具,它的主要作用就是使當(dāng)前線程等待一條或多條線程執(zhí)行完畢后再執(zhí)行當(dāng)前線程。同時(shí)提供了兩個(gè)主要方法來(lái)控制線程的交替執(zhí)行

// 創(chuàng)建CountDownLatch
CountDownLatch cdl = new CountDownLatch(1);
cdl.await()// 讓出cpu,使當(dāng)前線程等待
cdl.CountDown() // 計(jì)數(shù)器減1,只有當(dāng)計(jì)數(shù)器為零時(shí)才會(huì)喚醒被await的線程

CountDownLatch提供了一個(gè)構(gòu)造器用于參數(shù)Count,在創(chuàng)建時(shí)就給定計(jì)數(shù)個(gè)數(shù)。每次調(diào)用CountDown方法就減一知道減為0時(shí)才會(huì)執(zhí)行被await等待的線程。

我們來(lái)看下面這個(gè)示例,目的是順序打印出“A、B、C”

public class Demo02 {
    public static void main(String[] args) {
        CountDownLatch count = new CountDownLatch(1);
        new ThreadA(count).start();
        new ThreadB(count).start();
    }
}
class ThreadA extends Thread{
    private CountDownLatch count;
    public ThreadA(CountDownLatch count) {
        this.count = count;
    }
    @Override
    public void run() {
        System.out.println("A");
        // 使當(dāng)前線程等待  等待打印B之后宰繼續(xù)執(zhí)行打印A
        try {
            count.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("C");
    }
}
class ThreadB extends Thread{
    private CountDownLatch count;
    public ThreadB(CountDownLatch count) {
        this.count = count;
    }
    @Override
    public void run() {
        System.out.println("B");
        // 當(dāng)前線程執(zhí)行完后倒計(jì)數(shù)減一
        count.countDown();
    }
}

但是有序線程執(zhí)行先后 順序不確定,也有可能打印出“B、A、C”

CyclicBarrier循環(huán)屏障

CyclicBarrier與CountDownLatch很容易弄混

CountDownLatch:使一條或多條線程等待其他線程執(zhí)行完畢之后再執(zhí)行自己,內(nèi)部使用倒計(jì)數(shù),最終執(zhí)行被await等待的線程

CyclicBarrier:阻塞一個(gè)線程組,內(nèi)部采用正計(jì)數(shù)。當(dāng)被阻塞的線程達(dá)到某個(gè)數(shù)量時(shí)才能執(zhí)行指定的任務(wù)。我們每調(diào)用一次await代表阻塞了一條線程。

假設(shè)示例:五個(gè)人進(jìn)入會(huì)議室執(zhí)行開會(huì)任務(wù)

// 六條線程:五個(gè)員工進(jìn)入會(huì)議室、一個(gè)開會(huì)
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // 創(chuàng)建循環(huán)屏障
        CyclicBarrier cb = new CyclicBarrier(5,new Metting());
        for (int i = 1; i <= 4; i++) {
            new Employee(i+"號(hào)員工",cb).start();
        }
    }
}
class Employee extends Thread{
    private CyclicBarrier cb;
    public Employee(String s, CyclicBarrier cb) {
        super(s);
        this.cb = cb;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"進(jìn)入會(huì)議室");

        try {
            Thread.sleep(1000);
            cb.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class Metting implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"組織會(huì)議,會(huì)議開始");
    }
}

上述代碼所示:

CyclicBarrier cb = new CyclicBarrier(5,new Metting());

我們創(chuàng)建了一個(gè)循環(huán)屏障用于控制線程執(zhí)行,當(dāng)被await阻塞的線程數(shù)==5時(shí)將會(huì)執(zhí)行newMetting的Runnable線程任務(wù)

同時(shí)會(huì)發(fā)現(xiàn)最后一個(gè)到達(dá)會(huì)議室的人(線程)將會(huì)組織會(huì)議開始,這說(shuō)明我們調(diào)用了await方法并不是將該線程阻塞。是由于CyclicBarrier底層由線程池實(shí)現(xiàn),每一條線程執(zhí)行完畢之后都會(huì)被線程池回收而不是阻塞

Semaphore指示燈

Semaphore用于設(shè)置一個(gè)或多個(gè)線程可以同時(shí)執(zhí)行即控制線程的并發(fā)數(shù)量,其他線程被阻塞。常用于限流操作。同時(shí)可以設(shè)置公平鎖和非公平鎖

Semaphore的使用與Lock工具有些類似,同樣是提供了兩個(gè)方法用于上鎖和解鎖。只是Semaphore可以自由的控制能拿到鎖的線程數(shù)

Semaphore提供了如下兩個(gè)構(gòu)造器

public Semaphore(int permits) // permits為允許執(zhí)行的線程數(shù)
public Semaphore(int permits, boolean fair)
    // fair為true表示公平鎖,等待時(shí)間最長(zhǎng)的線程將在下次進(jìn)入 反之是不公平鎖

Semphore提供的兩個(gè)操作鎖方法

public void acquire()  // 表示獲得許可
public void release()  // 表示釋放許可

示例:

public class SemaphoreDemo {
    public static void main(String[] args) {
        // 創(chuàng)建任務(wù)
        Service service = new Service();
        for (int i = 1; i <= 5; i++) {
            new MyThread(i+"號(hào)線程",service).start();
        }
    }
}
// 線程類
class MyThread extends Thread{
    private Service service;
    public MyThread(String name,Service service){
        super(name);
        this.service = service;
    }
    @Override
    public void run() {
        try {
            service.testMethod();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
// 抽離業(yè)務(wù)代碼
class Service{
    // 創(chuàng)建Semaphore對(duì)象 并指定線程數(shù)
    private Semaphore sp = new Semaphore(2);
    public void testMethod() throws Exception {
        // 獲取許可
        sp.acquire();
        System.out.println(Thread.currentThread().getName()+"進(jìn)入  時(shí)間:"+System.currentTimeMillis());
        Thread.sleep(200);
        System.out.println(Thread.currentThread().getName()+"執(zhí)行成功");
        System.out.println(Thread.currentThread().getName()+"離開  時(shí)間:"+System.currentTimeMillis());
        // 釋放許可
        sp.release();
    }
}

如上述程序所示,我們?cè)趧?chuàng)建Semaphore時(shí)指定了允許的并發(fā)數(shù)量為2,那么業(yè)務(wù)代碼同時(shí)只能被兩個(gè)線程執(zhí)行,一旦一條線程執(zhí)行完畢之后將會(huì)釋放許可,立刻會(huì)有其他線程獲得許可進(jìn)入執(zhí)行

Exchanger交換者

Exchanger用于線程間的通信、數(shù)據(jù)交換。Exchanger提供了一個(gè)同步點(diǎn)exchange方法:public V exchange(V x)互相交換數(shù)據(jù)的兩條線程必須都運(yùn)行到了同步點(diǎn)才能執(zhí)行交換數(shù)據(jù)的操作,只有一方到達(dá)時(shí)就會(huì)進(jìn)行等待,等待時(shí)間可以由開發(fā)人員設(shè)定

我們先來(lái)看下面的示例

public class ExchangerDemo {
    public static void main(String[] args) {
        // 創(chuàng)建交換者
        Exchanger<String> exchanger = new Exchanger<>();
        // 創(chuàng)建兩條線程進(jìn)行交換數(shù)據(jù)
        new ThreadN("線程N(yùn)",exchanger).start();
        new ThreadP("線程P",exchanger).start();
    }
}
class ThreadN extends Thread{
    private Exchanger<String> exchanger;
    public ThreadN(String name,Exchanger<String> exchanger) {
        super(name);
        this.exchanger = exchanger;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"給線程P:"+"我是線程N(yùn)");
        try {
            String exchange = exchanger.exchange("我是線程N(yùn)");
            System.out.println("線程N(yùn)拿到數(shù)據(jù):"+exchange);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
class ThreadP extends Thread{
    private Exchanger<String> exchanger;
    public ThreadP(String name,Exchanger<String> exchanger) {
        super(name);
        this.exchanger = exchanger;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"給線程N(yùn):"+"我是線程P");
        try {
            String exchange = exchanger.exchange("我是線程P");
            System.out.println("線程P拿到數(shù)據(jù):"+exchange);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

根據(jù)最終打印,可以發(fā)現(xiàn)兩者交換了數(shù)據(jù)。這兩條線程擁有的是同一個(gè)交換者對(duì)象,所以可以實(shí)現(xiàn)數(shù)據(jù)交換。

前文提到過(guò)我們可以自定義線程等待的時(shí)間,就是再同步點(diǎn)exchange處等待另一條線程執(zhí)行到此的時(shí)間。利用exchange方法定義等待時(shí)間

public V exchange(V x, long timeout, TimeUnit unit)
    // timeout等待的時(shí)間數(shù)值  unit時(shí)間單位
    // 示例:只等待五秒
exchanger.exchange("111","5000", TimeUnit.SECONDS)

超出了規(guī)定的等待時(shí)間,正在等待的線程將被回收并拋出java.util.TimeoutException超時(shí)異常,所以交換數(shù)據(jù)的雙方必須都執(zhí)行到同步點(diǎn)才能進(jìn)行數(shù)據(jù)交換

到此這篇關(guān)于Java current并發(fā)包超詳細(xì)分析的文章就介紹到這了,更多相關(guān)Java current并發(fā)包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論