Java并发编程的艺术,解读并发编程的优缺点
并發編程的優缺點
使用并發的原因
多核的CPU的背景下,催生了并發編程的趨勢,通過并發編程的形式可以將多核CPU的計算能力發揮到極致,性能得到提升。
在特殊的業務場景下先天的就適合于并發編程。 比如在圖像處理領域,一張1024X768像素的圖片,包含達到78萬6千多個像素。即時將所有的像素遍歷一邊都需要很長的時間, 面對如此復雜的計算量就需要充分利用多核的計算的能力。又比如當我們在網上購物時,為了提升響應速度,需要拆分,減庫存, 生成訂單等等這些操作,就可以進行拆分利用多線程的技術完成。?面對復雜業務模型,并行程序會比串行程序更適應業務需求,而并發編程更能吻合這種業務拆分。
并發編程的缺點
頻繁的上下文切換
時間片是CPU分配給各個線程的時間,因為時間非常短,所以CPU不斷通過切換線程,讓我們覺得多個線程是同時執行的,時間片一般是幾十毫秒。 而每次切換時,需要保存當前的狀態起來,以便能夠進行恢復先前狀態,而這個切換時非常損耗性能, 過于頻繁反而無法發揮出多線程編程的優勢。 通常減少上下文切換可以采用無鎖并發編程,CAS算法,使用最少的線程和使用協程。
無鎖并發編程:可以參照concurrentHashMap鎖分段的思想,不同的線程處理不同段的數據, 這樣在多線程競爭的條件下,可以減少上下文切換的時間
CAS算法,利用Atomic下使用CAS算法來更新數據,使用了樂觀鎖,可以有效的減少一部分不必要的鎖競爭帶來的上下文切換
使用最少線程:避免創建不需要的線程,比如任務很少,但是創建了很多的線程,這樣會造成大量的線程都處于等待狀態
協程:在單線程里實現多任務的調度,并在單線程里維持多個任務間的切換
由于上下文切換也是個相對比較耗時的操作,所以在《Java并發編程的藝術》一書中有過一個實驗,并發累加未必會比串行累加速度要快。 可以使用Lmbench3測量上下文切換的時長,vmstat測量上下文切換次數。
線程安全
多線程編程中最難以把握的就是臨界區線程安全問題,稍微不注意就會出現死鎖的情況,一旦產生死鎖就會造成系統功能不可用。
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()?{@Overridepublic?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()?{@Overridepublic?void?run()?{synchronized?(resource_b)?{System.out.println("get?resource?b");synchronized?(resource_a)?{System.out.println("get?resource?a");}}}});threadA.start();threadB.start();} }那么,通常可以用如下方式避免死鎖的情況:
避免一個線程同時獲得多個鎖;
避免一個線程在鎖內部占有多個資源,盡量保證每個鎖只占用一個資源;
嘗試使用定時鎖,使用lock.tryLock(timeOut),當超時等待時當前線程不會阻塞;
對于數據庫鎖,加鎖和解鎖必須在一個數據庫連接里,否則會出現解鎖失敗的情況
學習并發中遇到的一些概念
線程
線程是依附于進程的,?進程是分配資源的最小單位,一個進程可以生成多個線程,這些線程擁有共享的進程資源。 就每個線程而言,只有很少的獨有資源, 如:控制線程運行的線程控制塊,保留局部變量和少數參數的棧空間等。 在線程的生命周期中,它要經過新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked)和死亡(Dead)5種狀態。
?
同步 VS 異步
同步和異步通常用來形容一次方法調用。
同步調用,就是調用者必須等待被調用的方法結束后,調用者后面的代碼才能執行。
異步調用,就是調用者不用管被調用方法是否完成,都會繼續執行后面的代碼,當被調用的方法完成后會通知調用者。
來個比喻:超市購物和網上購物
同步調用,就像在超市購物,如果一件物品沒了,你得等倉庫人員跟你調貨,直到倉庫人員跟你把貨物送過來,你才能去收銀臺付款。
異步調用,就像網購,你在網上付款下單后,什么事就不用管了,該干嘛就干嘛去了,當貨物到達后你收到通知去取就好。
并發與并行
并發和并行的區別就是一個處理器同時處理多個任務和多個處理器或者是多核的處理器同時處理多個不同的任務。
并發是邏輯上的同時發生
并行是物理上的同時發生。
來個比喻:并發和并行的區別就是一個人同時吃三個饅頭和三個人同時吃三個饅頭。
下圖反映了一個包含8個操作的任務在一個有兩核心的CPU中創建四個線程運行的情況。 假設每個核心有兩個線程,那么每個CPU中兩個線程會交替并發,兩個CPU之間的操作會并行運算。 單就一個CPU而言兩個線程可以解決線程阻塞造成的不流暢問題,其本身運行效率并沒有提高, 多CPU的并行運算才真正解決了運行效率問題,這也正是并發和并行的區別。
?
阻塞和非阻塞
阻塞和非阻塞通常用來形容多線程間的相互影響。 比如一個線程占有了臨界區資源,那么其他線程需要這個資源就必須進行等待該資源的釋放, 會導致等待的線程掛起,這種情況就是阻塞, 而非阻塞就恰好相反,它強調沒有一個線程可以阻塞其他線程,所有的線程都會嘗試地往前運行。
臨界區
臨界區用來表示一種公共資源或者說是共享數據,可以被多個線程使用。 但是每個線程使用時,一旦臨界區資源被一個線程占有,那么其他線程必須等待。
轉載于:https://blog.51cto.com/13672983/2394984
總結
以上是生活随笔為你收集整理的Java并发编程的艺术,解读并发编程的优缺点的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nodejs的事件循环1
- 下一篇: java美元兑换,(Java实现) 美元