溫馨提示×

溫馨提示×

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

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

Java并發(fā)編程的藝術(shù),解讀并發(fā)編程的優(yōu)缺點(diǎn)

發(fā)布時(shí)間:2020-06-29 09:44:07 來源:網(wǎng)絡(luò) 閱讀:433 作者:Java萌新 欄目:編程語言

并發(fā)編程的優(yōu)缺點(diǎn)

使用并發(fā)的原因

多核的CPU的背景下,催生了并發(fā)編程的趨勢,通過并發(fā)編程的形式可以將多核CPU的計(jì)算能力發(fā)揮到極致,性能得到提升。
在特殊的業(yè)務(wù)場景下先天的就適合于并發(fā)編程。 比如在圖像處理領(lǐng)域,一張1024X768像素的圖片,包含達(dá)到78萬6千多個(gè)像素。即時(shí)將所有的像素遍歷一邊都需要很長的時(shí)間, 面對如此復(fù)雜的計(jì)算量就需要充分利用多核的計(jì)算的能力。又比如當(dāng)我們在網(wǎng)上購物時(shí),為了提升響應(yīng)速度,需要拆分,減庫存, 生成訂單等等這些操作,就可以進(jìn)行拆分利用多線程的技術(shù)完成。 面對復(fù)雜業(yè)務(wù)模型,并行程序會(huì)比串行程序更適應(yīng)業(yè)務(wù)需求,而并發(fā)編程更能吻合這種業(yè)務(wù)拆分。
并發(fā)編程的缺點(diǎn)

頻繁的上下文切換

時(shí)間片是CPU分配給各個(gè)線程的時(shí)間,因?yàn)闀r(shí)間非常短,所以CPU不斷通過切換線程,讓我們覺得多個(gè)線程是同時(shí)執(zhí)行的,時(shí)間片一般是幾十毫秒。 而每次切換時(shí),需要保存當(dāng)前的狀態(tài)起來,以便能夠進(jìn)行恢復(fù)先前狀態(tài),而這個(gè)切換時(shí)非常損耗性能, 過于頻繁反而無法發(fā)揮出多線程編程的優(yōu)勢。 通常減少上下文切換可以采用無鎖并發(fā)編程,CAS算法,使用最少的線程和使用協(xié)程。

無鎖并發(fā)編程:可以參照concurrentHashMap鎖分段的思想,不同的線程處理不同段的數(shù)據(jù), 這樣在多線程競爭的條件下,可以減少上下文切換的時(shí)間
CAS算法,利用Atomic下使用CAS算法來更新數(shù)據(jù),使用了樂觀鎖,可以有效的減少一部分不必要的鎖競爭帶來的上下文切換
使用最少線程:避免創(chuàng)建不需要的線程,比如任務(wù)很少,但是創(chuàng)建了很多的線程,這樣會(huì)造成大量的線程都處于等待狀態(tài)
協(xié)程:在單線程里實(shí)現(xiàn)多任務(wù)的調(diào)度,并在單線程里維持多個(gè)任務(wù)間的切換
由于上下文切換也是個(gè)相對比較耗時(shí)的操作,所以在《Java并發(fā)編程的藝術(shù)》一書中有過一個(gè)實(shí)驗(yàn),并發(fā)累加未必會(huì)比串行累加速度要快。 可以使用Lmbench4測量上下文切換的時(shí)長,vmstat測量上下文切換次數(shù)。

線程安全

多線程編程中最難以把握的就是臨界區(qū)線程安全問題,稍微不注意就會(huì)出現(xiàn)死鎖的情況,一旦產(chǎn)生死鎖就會(huì)造成系統(tǒng)功能不可用。


public class DeadLockDemo {
    private static String resource_a = "A";
    private static String resource_b = "B";

    public static void main(String[] args) {
        deadLock();
    }

    public static void deadLock() {
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resource_a) {
                    System.out.println("get resource a");
                    try {
                        Thread.sleep(3000);
                        synchronized (resource_b) {
                            System.out.println("get resource b");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resource_b) {
                    System.out.println("get resource b");
                    synchronized (resource_a) {
                        System.out.println("get resource a");
                    }
                }
            }
        });
        threadA.start();
        threadB.start();

    }

}
那么,通??梢杂萌缦路绞奖苊馑梨i的情況:

避免一個(gè)線程同時(shí)獲得多個(gè)鎖;
避免一個(gè)線程在鎖內(nèi)部占有多個(gè)資源,盡量保證每個(gè)鎖只占用一個(gè)資源;
嘗試使用定時(shí)鎖,使用lock.tryLock(timeOut),當(dāng)超時(shí)等待時(shí)當(dāng)前線程不會(huì)同步 VS 異步

同步和異步通常用來形容一次方法調(diào)用。

同步調(diào)用,就是調(diào)用者必須等待被調(diào)用的方法結(jié)束后,調(diào)用者后面的代碼才能執(zhí)行。
異步調(diào)用,就是調(diào)用者不用管被調(diào)用方法是否完成,都會(huì)繼續(xù)執(zhí)行后面的代碼,當(dāng)被調(diào)用的方法完成后會(huì)通知調(diào)用者。
來個(gè)比喻:超市購物和網(wǎng)上購物

同步調(diào)用,就像在超市購物,如果一件物品沒了,你得等倉庫人員跟你調(diào)貨,直到倉庫人員跟你把貨物送過來,你才能去收銀臺(tái)付款。

異步調(diào)用,就像網(wǎng)購,你在網(wǎng)上付款下單后,什么事就不用管了,該干嘛就干嘛去了,當(dāng)貨物到達(dá)后你收到通知去取就好。

并發(fā)與并行

并發(fā)和并行的區(qū)別就是一個(gè)處理器同時(shí)處理多個(gè)任務(wù)和多個(gè)處理器或者是多核的處理器同時(shí)處理多個(gè)不同的任務(wù)。

并發(fā)是邏輯上的同時(shí)發(fā)生
并行是物理上的同時(shí)發(fā)生。
并發(fā)性(concurrency),又稱共行性,是指能處理多個(gè)同時(shí)性活動(dòng)的能力,并發(fā)事件之間不一定要同一時(shí)刻發(fā)生。
并行(parallelism)是指同時(shí)發(fā)生的兩個(gè)并發(fā)事件,具有并發(fā)的含義,而并發(fā)則不一定并行。
來個(gè)比喻:并發(fā)和并行的區(qū)別就是一個(gè)人同時(shí)吃三個(gè)饅頭和三個(gè)人同時(shí)吃三個(gè)饅頭。

下圖反映了一個(gè)包含8個(gè)操作的任務(wù)在一個(gè)有兩核心的CPU中創(chuàng)建四個(gè)線程運(yùn)行的情況。 假設(shè)每個(gè)核心有兩個(gè)線程,那么每個(gè)CPU中兩個(gè)線程會(huì)交替并發(fā),兩個(gè)CPU之間的操作會(huì)并行運(yùn)算。 單就一個(gè)CPU而言兩個(gè)線程可以解決線程阻塞造成的不流暢問題,其本身運(yùn)行效率并沒有提高, 多CPU的并行運(yùn)算才真正解決了運(yùn)行效率問題,這也正是并發(fā)和并行的區(qū)別。阻塞;
對于數(shù)據(jù)庫鎖,加鎖和解鎖必須在一個(gè)數(shù)據(jù)庫連接里,否則會(huì)出現(xiàn)解鎖失敗的情況
學(xué)習(xí)并發(fā)中遇到的一些概念

線程

線程是依附于進(jìn)程的, 進(jìn)程是分配資源的最小單位,一個(gè)進(jìn)程可以生成多個(gè)線程,這些線程擁有共享的進(jìn)程資源。 就每個(gè)線程而言,只有很少的獨(dú)有資源, 如:控制線程運(yùn)行的線程控制塊,保留局部變量和少數(shù)參數(shù)的棧空間等。 在線程的生命周期中,它要經(jīng)過新建(New)、就緒(Runnable)、運(yùn)行(Running)、阻塞(Blocked)和死亡(Dead)5種狀態(tài)。
Java并發(fā)編程的藝術(shù),解讀并發(fā)編程的優(yōu)缺點(diǎn)

阻塞和非阻塞

阻塞和非阻塞通常用來形容多線程間的相互影響。 比如一個(gè)線程占有了臨界區(qū)資源,那么其他線程需要這個(gè)資源就必須進(jìn)行等待該資源的釋放, 會(huì)導(dǎo)致等待的線程掛起,這種情況就是阻塞, 而非阻塞就恰好相反,它強(qiáng)調(diào)沒有一個(gè)線程可以阻塞其他線程,所有的線程都會(huì)嘗試地往前運(yùn)行。

臨界區(qū)

臨界區(qū)用來表示一種公共資源或者說是共享數(shù)據(jù),可以被多個(gè)線程使用。 但是每個(gè)線程使用時(shí),一旦臨界區(qū)資源被一個(gè)線程占有,那么其他線程必須等待。

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

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

AI