java併發編程(一) 入門篇----爲什麼要使用併發編程

一. 爲什麼要使用併發編程?

併發編程的主要目的是爲了使程序運行的更快;

但是下一個問題出來了,併發編程一定會使我們的程序運行的更快嗎?爲什麼併發編程會使我們的程序變得更快呢?

二. 理解併發編程

2.1併發編程一定會使我們的程序運行的更快嗎?

答案是並不是,併發編程只有在一定的請求量或者計算量的時候纔會顯示出優勢;

2.2 爲什麼有的時候多線程反而會變慢呢?

我們知道os(操作系統)執行併發操作是採用的時間片輪轉算法,簡單的說就是線程1先執行10ms 然後線程B在執行10ms 然後線程A又執行了10ms 線程A執行完畢 線程B又開始執行到結束

我們可以發現這種執行的機制,會導致cpu一會執行A線程 一會執行B線程,這就導出了一個第一個導致多線程慢的因素:線程的上下文切花需要時間
補充:Java創建多線程需要OS的干預,OS需要從用戶態切換到核心態 這個過程很耗費時間

那麼簡單的理解就是:如果多線程執行的速度快於單線程執行的速度,那麼我們可以理解爲多線程一定會在執行前做一些準備,而單線程不需要,所以當任務數或者工作量並不大的時候單線程就會很有優勢;但是當任務數上來後多線程的優勢就體現出來了;
這裏我們有個可以繼續深入研究的點:即具體什麼時候,什麼情況下單線程執行優於多線程

2.3 那麼如何提高併發編程的性能呢?

我們知道併發性能的瓶頸就是頻繁的上下文切換,那麼我們就可以通過減少上下文的切換來提高性能。

減少上下文切換的方法又一下幾種

1. 採用無鎖併發編程:多線程競爭鎖的時候就會引起上下文的切換,所以處理數據的時候應該避免加鎖,可以把數據分段,每個線程來處理一段的數據,
2. CAS算法: Java的Atomic包使用CAS算法來更新數據,不需要加鎖

**3. 儘量合理的配置線程即使用最少的線程:**例如我們處理一個任務10個線程100ms搞定,那麼你開100000個線程來處理,光上下文切換的時間都要超過100ms

**4. 使用協程:**在單線程裏實現任務的調度,並在單線程裏維持多個任務的切換

2.2 爲什麼併發編程會使我們的程序變得更快呢?

首先對於單線程來說,程序是一行一行的執行的,所以假設在120行-180行之間的程序含有數據庫操作需要等待很長時間,那麼180行以後的代碼都必須等到180行的代碼執行完才能繼續執行,而併發編程其實就是異步化,也就是主線程在執行到120行的時候,120-180行的代碼交給另一個線程去執行,主線程可以繼續向下執行,所以兩件事是並行的,所以程序的響應時間就會變快

三.死鎖

死鎖是導致系統功能不可用的重要原因之一;
什麼是死鎖呢?
線程1在等待線程2釋放鎖,線程2也在等線程1釋放另一個鎖,最終導致線程1和2都在等待中,從而不能繼續執行程序;

構成死鎖的必要條件有一下幾個

  1. 互斥
  2. 不可剝奪
  3. 保持申請
  4. 循環等待
    互斥:也就是加互斥鎖,線程1獲取到了對資源1的鎖,那麼線程2就不能獲取對資源1的鎖了
    不可剝奪 線程1對資源1加的鎖,線程2不可以撤銷線程1對資源1加的鎖
    保持申請 線程1一直持有對資源1的鎖
    循環等待 線程1等線程2 線程2等線程1
代碼行 線程1的操作 線程2 的操作
100行 對資源1加鎖 其他操作
110行 執行其他操作 對資源2加鎖
120行 獲取資源2 因爲有鎖所以等待 獲取資源1 因爲有鎖所以等待
121行 因爲線程在等待 所以此行不會執行 因爲線程在等待 所以此行不會執行

我們可以看到線程1和2都在120行處等待,所以下面的代碼不會執行, 這就是死鎖

3.1 如何避免死鎖呢?

  1. 避免一個線程同時獲取多個鎖
  2. 避免一個線程同時佔用多個資源
  3. 嘗試使用定時鎖來代替內部的鎖機制
  4. 對於數據庫鎖,加鎖和解鎖必須在同一個連接內,否則解鎖會失敗

四.多線程需要考慮的問題

即使我們使用了多線程,但是系統還是會受到硬盤的讀寫速度,網絡帶寬,cpu處理速度等的影響
所以在設置線程數的時候也要綜合考慮

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章