Java多線程學習(七)併發編程中一些問題

最近私下作一項目,一bug幾日未解決,總惶恐。一日頓悟,bug不可怕,怕的是項目不存在bug,與其害怕,何不與其剛正面。web

系列文章傳送門:面試

Java多線程學習(一)Java多線程入門算法

Java多線程學習(二)synchronized關鍵字(1)數據庫

Java多線程學習(二)synchronized關鍵字(2)編程

Java多線程學習(三)volatile關鍵字服務器

Java多線程學習(四)等待/通知(wait/notify)機制微信

Java多線程學習(五)線程間通訊知識點補充多線程

Java多線程學習(六)Lock鎖的使用併發

本節思惟導圖:
思惟導圖
關注微信公衆號:「Java面試通關手冊」 回覆「Java多線程」獲取思惟導圖源文件和思惟導圖軟件。socket

多線程就必定好嗎?快嗎??

併發編程的目的就是爲了能提升程序的執行效率提升程序運行速度,可是併發編程並不老是能提升程序運行速度的,並且併發編程可能會遇到不少問題,好比:內存泄漏、上下文切換、死鎖還有受限於硬件和軟件的資源閒置問題。

多線程就是幾乎同時執行多個線程(一個處理器在某一個時間點上永遠都只能是一個線程!即便這個處理器是多核的,除非有多個處理器才能實現多個線程同時運行)。CPU經過給每一個線程分配CPU時間片來實現僞同時運行,由於CPU時間片通常很短很短,因此給人一種同時運行的感受。

上下文切換

當前任務在執行完CPU時間片切換到另外一個任務以前會先保存本身的狀態,以便下次再切換會這個任務時,能夠再加載這個任務的狀態。任務從保存到再加載的過程就是一次上下文切換

上下文切換一般是計算密集型的。也就是說,它須要至關可觀的處理器時間,在每秒幾十上百次的切換中,每次切換都須要納秒量級的時間。因此,上下文切換對系統來講意味着消耗大量的 CPU 時間,事實上,多是操做系統中時間消耗最大的操做。
Linux相比與其餘操做系統(包括其餘類 Unix 系統)有不少的優勢,其中有一項就是,其上下文切換和模式切換的時間消耗很是少。

那麼咱們如今可能會考慮 :如何減小上下文切換的次數呢???

減小上下文切換

減小上下文切換
這是《Java併發編程的藝術》的做者方騰飛大佬嗎????

上下文切換又分爲2種:讓步式上下文切換搶佔式上下文切換。前者是指執行線程主動釋放CPU,與鎖競爭嚴重程度成正比,可經過減小鎖競爭和使用CAS算法來避免;後者是指線程因分配的時間片用盡而被迫放棄CPU或者被其餘優先級更高的線程所搶佔,通常因爲線程數大於CPU可用核心數引發,可經過適當減小線程數使用協程來避免。

總結一下:

  1. 減小鎖的使用。由於多線程競爭鎖時會引發上下文切換。
  2. 使用CAS算法。這種算法也是爲了減小鎖的使用。CAS算法是一種無鎖算法。
  3. 減小線程的使用。人物不多的時候建立大量線程會致使大量線程都處於等待狀態。
  4. 使用協程

咱們上面提到了兩個名詞:「CAS算法」「協程」。可能有些人不是很瞭解這倆東西,因此這裏簡單說一下。。。

CAS算法

CAS(比較與交換,Compare and swap) 是一種有名的無鎖算法。無鎖編程,即不使用鎖的狀況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的狀況下實現變量的同步,因此也叫非阻塞同步(Non-blocking Synchronization)。實現非阻塞同步的方案稱爲「無鎖編程算法」( Non-blocking algorithm)。
相對應的,獨佔鎖是一種悲觀鎖,synchronized就是一種獨佔鎖,它假設最壞的狀況,而且只有在確保其它線程不會形成干擾的狀況下執行,會致使其它全部須要鎖的線程掛起,等待持有鎖的線程釋放鎖。

協程
維基百科中的協程

協程也能夠說是微線程或者說是輕量級的線程,它佔用的內存更少而且更靈活。不少編程語言中都有協程。Lua, Ruby 等等都有本身的協程實現。Go徹底就是由於協程而發展壯大的。維基百科上面並無Java實現協程的方式,可是不表明Java不能實現協程。好比可使用Java實現的開源協程庫:Quasar。Quasar官網: http://www.paralleluniverse.co/quasar/,。這個庫實現了一種能夠和Go語言中的Goroutine相對標的編程概念:Fiber。Fiber是一種真正的協程。

最後Mark兩篇關於協程的文章:

協程,高併發IO終極殺器(3):https://zhuanlan.zhihu.com/p/27590299

次時代Java編程(一):Java裏的協程:http://geek.csdn.net/news/detail/71824

避免死鎖

在操做系統中,死鎖是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。

在線程中,若是兩個線程同時等待對方釋放鎖也會產生死鎖。

鎖是一個好東西,可是使用不當就會形成死鎖。一旦死鎖產生程序就沒法繼續運行下去。因此如何避免死鎖的產生,在咱們使用併發編程時相當重要。

根據《Java併發編程的藝術》有下面四種避免死鎖的常見方法:

  • 避免一個線程同時得到多個鎖
  • 避免一個線程在鎖內同時佔用多個資源,儘可能保證每一個鎖只佔用一個資源
  • 嘗試使用定時鎖,使用lock.tryLock(timeout)來替代使用內部鎖機制
  • 對於數據庫鎖,加鎖和解鎖必須在一個數據庫鏈接裏,不然會出現解鎖失敗的狀況

解決資源限制

這裏我以爲《Java併發編程的藝術》講的仍是挺好的。

什麼是資源限制???

所謂資源限制就是咱們在進行併發編程時,程序的運行速度受限於計算機硬件資源好比CPU,內存等等或軟件資源好比軟件的質量、性能等等。舉個例子:若是說服務器的帶寬只有2MB/s,某個資源的下載速度是1MB/s,系統啓動10個線程下載該資源並不會致使下載速度編程10MB/s,因此在併發編程時,須要考慮這些資源的限制。硬件資源限制有:帶寬的上傳和下載速度、硬盤讀寫速度和CPU處理速度;軟件資源限制有數據庫的鏈接數、socket鏈接數、軟件質量和性能等等。

資源限制引起的問題

在併發編程中,程序運行加快的緣由是運行方式從串行運行變爲併發運行,可是若是若是某段程序的併發執行因爲資源限制仍然在串行執行的話,這時候程序的運行不只不會加快,反而會更慢,由於可能增長了上下文切換和資源調度的時間。

如何解決資源限制的問題

對於硬件資源限制,能夠考慮使用集羣並行執行程序。既然單機的資源有限制,那麼就讓程序在多機上運行。好比使用Hadoop或者本身搭建服務器集羣。

對於軟件資源的限制,能夠考慮使用資源池將資源複用。好比使用鏈接池將數據庫和Socket複用,或者在調用對方webservice接口獲取數據時,只創建一個鏈接。另外還能夠考慮使用良好的開源軟件。

在資源限制的狀況下如何進行併發編程

根據不一樣的資源限制調整程序的併發度,好比下載文件程序依賴於兩個資源-帶寬和硬盤讀寫速度。有數據庫操做時,設計數據庫練鏈接數,若是SQL語句執行很是快,而線程的數量比數據庫鏈接數大不少,則某些線程會被阻塞,等待數據庫鏈接。

參考:

維基百科,百度百科

《Java併發編程的藝術》

上下文切換的詳解:http://ifeve.com/context-swit...

相關文章
相關標籤/搜索