Java高併發與多線程(一)-----概念

其實以前一直想專門寫一篇,單獨說一說Java的多線程與高併發,可是一直以來,都沒有想到可以用什麼比較有趣的表現形式去表達出來,並且網上充斥着不少相似的博客,有好的又很差的,有簡介的有繁瑣的,因此也一直沒寫。 java

可是想了想既然以前有這個想法,並且也已經很久沒有寫過博客了,索性仍是寫一寫,儘可能寫的有意思一點。 web

   

另:以前的高併發&性能優化沒有來得及往下寫,實在是由於裏面的東西太過於複雜,且最近正好換了工做,確實沒有那麼多時間去研究,如今寫東西仍是但願能多寫點有用的,而不是書本照搬當筆記用,固然能力有限,寫爛了,你們諒解一二,略過不看便可。 算法

   

固然第一篇,咱們依舊從概念開始,因此第一部分還是概念。 數據庫

   

【併發與並行】 緩存

從題目名詞開始講。 性能優化

  • 併發

併發,顧名思義,一塊兒出發;微信

在你吃飯的時候,來了一個電話,若是你能夠先接電話,而後再繼續把飯吃完,這個叫併發;網絡

可是若是你只能等飯吃完才能夠去接電話,叫非併發(串行)。多線程

因此,併發指的是處理多任務的能力,當你只能一件事情一件事情串行執行任務的時候,就是不支持併發的,當你能夠多件事情一塊兒執行的時候(輪替或者其餘方式),就是支持併發的。架構

   

  • 並行

仍是舉上面那個例子,當你吃飯的時候,來了一個電話,你邊吃飯邊接電話,這叫並行;

並行指的就是同時運行;支持並行的基礎就是多線程。

   

【同步和異步】

同步和異步的概念通常用於方法

  • 同步  

    當一個方法開始執行,必須等這個方法執行結束,才能夠往下執行,咱們叫作同步。

    同步主要用於上下有遞進關係的代碼,特色是有序,串行執行,邏輯簡單,可是執行效率較低。

  • 異步     

    當一個方法開始執行,咱們沒必要等這個方法執行結束,直接執行後面的內容,咱們叫作異步。

    (能夠認爲只是進行了一個消息的傳遞,調用後會當即返回)

    異步在java裏面主要使用線程(包括一些封裝類也是如此)實現,特色是執行效率高,可是邏輯相對複雜,容易出問題

   

【什麼是高併發】

有果必有因,通俗來說,多線程能夠認爲是高併發的一種表現形式或者解決方案,因此在講多線程以前,咱們先講高併發。

高併發,指的是一個系統,在短期內,收到大量操做請求的狀況。

這種狀況,通常而言主要發生在web系統中,好比:京東雙十一,微博明星傳出緋聞,12306春運搶票等等。

 

很容易理解的東西咱們不過多的做詮釋,如下幾項,是高併發的經常使用指標:

  • 響應時間Response Time

    系統對請求做出的響應時間(一個請求從請求發出到請求結束的時間)

  • 吞吐量Throughput

    單位時間內處理的請求數量

  • 每秒查詢率QPSQuery Per Second

    每秒響應請求數。(其實與吞吐量指向同一個指標)

  • 併發用戶數

    同時承載正常使用系統功能的用戶數量。

   

這裏須要注意的是,咱們常常會將高併發和多線程放在一塊兒講,可是他們之間並不能劃等號,多線程只是高併發在應用代碼層面的一種解決方案,然而通常狀況下,高併發還須要系統架構,硬件設施,網絡等多方面的調優協助完成。

   

【雪崩效應】

雪崩效應,本來出如今密碼學中,後來引伸入高併發場景的一個概念。

在密碼學中,雪崩效應(Avalanche effect)指加密算法(尤爲是塊密碼和加密散列函數)的一種理想屬性。

雪崩效應是指當輸入發生最微小的改變(例如,反轉一個二進制位)時,也會致使輸出的劇變(如,輸出中一半的二進制位發生反轉)。

   

服務雪崩效應是一種因"服務提供者的不可用"(緣由)致使"服務調用者不可用"(結果),並將不可用逐漸放大的現象。

服務雪崩的過程能夠分爲三個階段:

  1. 服務提供者不可用;
  2. 重試加大請求流量;
  3. 服務調用者不可用;

   

------如何避免

橫向擴充服務------如今咱們能夠利用不少工具來保證服務不會掛掉,而後流量比較大的時候,能夠橫向擴充服務來保證業務的流暢。

限流(下個部分會講)

熔斷(下個部分會講)

   

【高併發的四大利器】

對於軟件系統而言,通常會有四大策略去保證應用的高併發:

  • 緩存(cache)

    把經常使用數據存儲到能夠快速獲取的區域(緩存區),以便重複利用,提升效率。

例如:從內存中讀取數據時,先將經常使用的數據存放到緩存區,硬盤直接從緩存區讀取。

   

在這地地方咱們要注意

咱們平時所說的緩衝(buffer),和緩存不是同一回事,緩衝指的是在數據流轉過程當中,不一樣層次數據速度不一致時,利用緩衝區來緩解上下層之間速度問題,增長速度。

例如:將數據寫入到內存時,先寫入緩衝區,內存則直接從緩衝區中讀取寫入,減小IO次數,增長速度,下降對磁盤的損耗。

不過他們本質上都是爲了提升效率

   

  • 降級

    當服務出現問題或影響到核心流程時,須要暫時屏蔽掉,待高峯事後或問題解決後再打開;

   

  • 限流

    限流是高併發裏面最重要也是最複雜的方法,當不可降級場景出現時,須要採用限流限制該場景的併發請求,有損服務而不是不服務。

經過對併發訪問/請求進行限速或者一個時間窗口內的的請求進行限速來保護系統,一旦達到限制速率則能夠拒絕服務、排隊或等待、降級

    • 超過閾值時策略

         定向到錯誤頁或告知沒有資源

         返回兜底數據或默認數據,如商品詳情頁庫存默認有貨

    • 常見限流場景

        線程池

數據庫鏈接池

併發請求數

接口調用速率

MQ的消費速率

    • 常見限流算法

        令牌桶:一個存放固定容量令牌的桶,按照固定速率往桶裏添加令牌,請求獲取令牌,令牌不足時拒絕請求。

漏桶:流入速率過快,超過桶的容量,拒絕請求。

計數器(簡單粗暴):當請求超過計數時,拒絕請求。

   

  • 熔斷

    降級每每表明系統功能部分不可用,熔斷表明的是徹底不可用。
    降級通常是客戶端處理熔斷是在服務端處理的
    服務熔斷通常是指軟件系統中,因爲某些緣由使得服務出現了過載現象,爲防止形成整個系統故障,從而採用的一種保護措施,因此不少地方把熔斷亦稱爲過載保護

   

【進程】

首先咱們看一下百度百科的解釋:

進程(Process)是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操做系統結構的基礎。

在早期面向進程設計的計算機結構中,進程是程序的基本執行實體

在當代面向線程設計的計算機結構中,進程是線程的容器

程序是指令、數據及其組織形式的描述,進程是程序的實體。

   

其實用大白話講,進程其實就是指在系統中正在運行的一個應用程序

好比:咱們電腦中運行的QQ,微信,LOL,都是一個進程。

   

進程主要有如下幾個特性:

  • 獨立性

    進程是系統中獨立存在的實體,它擁有本身獨立的資源和本身私有的地址空間。

    進程之間不能夠直接訪問資源和地址空間。

  • 動態性

    程序(App)是一個靜態的指令集和,進程是一個正在執行中的指令集合,進程擁有本身的生命週期和不一樣的生命形態。

  • 併發性

    多個進程能夠在單個處理器上併發執行,不會相互受到影響。(主要是依賴於線程和時間片)

一個進程裏面能夠由單個或者多個線程協同 完成任務。

   

【什麼是多線程】

  • 首先,什麼是線程?

    教科書說法,線程是操做系統可以進行運算調度的最小單元

一個進程能夠有多個線程,可是一個線程只有一個父進程;

線程能夠擁有本身的堆棧,程序計數器以及局部變量,可是不擁有系統資源。

   

其實呢,學過操做系統你們都知道,其實對於單核單CPU而言,同時是隻能運行一個任務的,也就是說,同時只能跑一個線程;

若是我們的CPU只能線性執行,就是當你運行一個線程的時候,這個線程可能要等待網絡,IO等相關的資源,這個時候CPU只能等待,這樣CPU強大的運算能力就沒有獲得發揮,因此,產生了一個時間片的概念;

    • 時間片

        CPU給每一個線程分配了一部分時間去運行,雖然CPU同時只能運行一個線程,可是咱們進行線程的快速切換以後,能夠模擬出一個CPU同時運行多個線程的場景(其實主要仍是CPU太快了),這樣的話能夠充分利用CPU計算速度快的優點。(對於時間片,有不少種不一樣的算法,有興趣能夠百度,這裏不講)

        在引入時間片之後,我們一塊CPU就能夠同時跑多個線程了。

   

因此什麼是多線程呢?

多線程指的是,在單個程序(或者進程)裏面,能夠運行多個不一樣的線程,執行不一樣的任務,最終完成整個程序的運行邏輯。
 

這裏須要注意的是,線程是進程的子集,不一樣的進程使用不一樣的內存空間,而全部的線程共享一片相同的內存空間

別把它和棧內存搞混,每一個線程都擁有單獨的棧內存用來存儲本地數據

   

【線程的狀態】

其實下面這些隨便找個教科書或者網上的教程都有,屬於廢話,可是爲了概念的完整性,仍是把它們貼在下面:

只須要關注帶顏色的內容

  • 新建狀態(New)

    線程對象被建立後,就進入了新建狀態。

   

  • 就緒狀態(Runnable)

    也被稱爲"可執行狀態"。線程對象被建立後,其它線程調用了該對象的start()方法,從而來啓動該線程。

   

  • 運行狀態(Running)

    線程獲取CPU權限進行執行。須要注意的是,線程只能從就緒狀態進入到運行狀態

   

  • 阻塞狀態(Blocked)

    阻塞狀態是線程由於某種緣由放棄CPU使用權,暫時中止運行。
    直到線程進入就緒狀態,纔有機會轉到運行狀態。
      - 等待阻塞

    經過調用線程的wait()方法,讓線程等待某工做的完成。

      - 同步阻塞

    線程在獲取synchronized同步鎖失敗(由於鎖被其它線程所佔用),它會進入同步阻塞狀態。

      - 其餘阻塞

    經過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。

   

  • 死亡狀態(Dead)

    線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

   

【多線程三要素】

  •  原子性

    即一個不可再被分割的顆粒。

在Java中原子性指的是一個或多個操做要麼所有執行成功要麼所有執行失敗

經典場景:張三向李四轉帳,扣錢和入錢操做,要麼所有完成,要麼所有完不成。

  • 有序性

    程序按照代碼的前後順序執行。

這個主要是由於CPU自己可能會對指令進行重排序,在某些須要嚴格控制順序的代碼中,須要保持其有序。

  • 可見性

    多個線程同時訪問某個變量的時候,其中一個線程對變量進行了修改,這個變量的新值能夠立刻同步到另外一個線程。

   

關於如何保障以上三要素,後面會講到。

   

關於高併發與多線程相關的概念,主要就是以上這些,在以後的內容中,會繼續寫到線程的實現方式和主要方法,鎖,多線程中的封裝類等相關內容,感謝。

相關文章
相關標籤/搜索