java多線程的入門小記


java的多線程java

java的多線程的概念,向來都是很複雜、籠統、抽象的。現實世界只有將知識點抽象事後纔能有效的傳播,可是傳播的過程當中,只有將抽象的知識點具象化,咱們才能習得。因此咱們會將個別內容點進行一個具象化進而解剖。當咱們理解完了以後最終將其抽象成一個個名詞:多線程、資源、鎖等。程序員

本文僅從如下的範圍內容來談談java的多線程。編程

1. 何爲線程,線程的做用多線程

2. 資源的控制,鎖的介紹工具

3. 線程池的做用性能

4. 多線程的經常使用工具和方法優化

1.何爲線程線程

官方解釋:線程是一個單一的順序控制流程3d

線程分主線程、子線程。由主線程來建立子線程來執行各類任務。cdn

舉例說明:以動漫「火影忍者」舉例說明,主線程就比如每個忍者,他們構成了最基本的忍者世界,一個忍者能夠按照任務的緩急、難易程度同時執行多個任務。同時忍者也能分身(調用自身查克拉)來分擔自身的任務,這就比如,忍者世界觀中的忍者主體,基本等同於程序中的主線程。主線程沒了,分身則主觀上不可控,就消失了。

由上圖可見,主線程與子線程的關係和忍者與分身的是很類似,也就是說,主線程能作的事,咱們都能讓子線程幫咱們作。忍者本身能作的事也能去靠分身去作。接下來,咱們來看兩段代碼。

代碼一和代碼二最終的結果都是隻作了一件事,向控制檯輸出「Hello World!」。可是代碼一是由主線程去作的;代碼二是由主線程建立的子線程去作的。這裏咱們能夠看出,主線程和子線程的本質上區分並不大,由於它們都執行相同的邏輯,這一點上並無進行區分。

程序若是都是按照單個線程的話,那麼全部任務的執行均是按照順序來進行(串行執行)。

而多線程的做用是能夠安排不一樣的線程執行不一樣的任務

上圖是一個理論值,咱們在某些任務密集的場景下,多線程的執行效率多數狀況下可能高於單線程的執行。

爲何說是可能,由於這裏建立子線程是會消耗性能的,也帶有時間消耗,若是設置不合理,單單建立子線程的時間成本就遠大於執行任務的時間成本,這一點要結合實際場景進行考慮。

舉例說明:火影裏的忍者也不會接到一批任務就立刻分身去一個個的作,他們也得結合任務的實際狀況來考慮使用分身。

1. 線程能夠執行正確的邏輯代碼

2. 線程的建立也伴隨着性能消耗,並非無消耗

2.資源的控制

java中的資源能夠理解爲,一個實例或基礎數據類型的變量的任何操做。實例或變量在這裏不能徹底算作資源,由於根據面向對象編程中的封裝性,代碼中直接將實例暴露出來給非本類的實例進行操做是一個大忌。

這裏咱們從如下資源和線程的關係進行解剖。

1. 一個線程能夠執行多個資源(串行)

2. 多個線程能夠執行多個資源(並行)

3. 單個資源能夠被一個線程執行(串線)

4. 單個資源能夠被多個線程執行(並行)

從這裏看,1和3沒什麼問題。由於這種機制下咱們確保了一個資源被一個線程執行(等同於一個任務被一個忍者(本體或分身都可)執行)。可是2和4就出現了一個現象,同一個資源被多個線程所操做,若是不加以控制,則會出現指定以外的執行結果或者直接產生死鎖。

舉例說明:兩個忍者都執行了同一個任務,去殺死鄰國的頭目,咱們假設忍者A過去殺死了頭目,忍者B後去的,發現頭目死了,那他接下來怎麼辦?算任務失敗仍是算完成了?

固然忍者B最後確定仍是回去覆命了,也算他任務成功,這是任務自己的規則和秩序所決定的,可是程序的世界是無秩序,須要程序員經過代碼去打造這個無序的世界從而造成秩序

資源自身必定要包含約束性和規則性才能被正確的使用。

java自己提供了資源被多個線程調度的控制方式。

·synchronized關鍵字

·Atomic包

·ReentrantLock

·Semaphore

·CountDownLatch

·CyclicBarrier

·Phaser

咱們經過一張圖表來概況瞭解一下。

對於資源的控制的方式無非就是一個「」 字。現實當中處處充斥着這樣的例子,例如一個城市的市長,按照規定只能有一個,誰上任,那麼市長這個資源位就被誰「鎖」住了。可是程序世界中的「鎖」和現實世界中的「鎖」差異很大。

·現實世界的鎖,是可見,它控制着某同樣可見的物體,好比:門、箱子等,而再由這些具備隔絕性質的物體去控制級別更高的資源,例如:門裏的東西、箱子裏的錢。也就是說現實世界的鎖是間接的控制資源。

·程序世界裏的鎖,則是一種更爲高級的抽象,它包含的對資源的各類維度的控制,咱們能夠將其理解爲「規則」,好比:某類資源在同一時刻只能有一個線程進行操做(同步性)、某類資源必須由多個線程同時操做(同步協做)、某類資源最多隻能有N個線程進行操做(資源調度許可證)。這些都是「規則」的運用。

java中有關於鎖的內容很是多,咱們這裏先用一張圖來簡要介紹一下,之後再着重篇幅去介紹每一個鎖的相關特性。

1. 任何實例對外提供的方法(儘可能避免對變量的直接操做)

2. 咱們須要在對外提供的方法內用「鎖」去控制方法內的被調度的規則。

3. 實行第二條以前必定要肯定當前的編程環境,是單線程的仍是多線程

3.線程池

用一段話形容線程和資源的關係那就是。某我的(

線程
)去作(
調度
)某件有要求和規則(
鎖策略
)的事(
資源
);根據這件事(
資源
)的要求和規則(
鎖策略
)去約束作這個事人(
線程
)的作法

咱們用各類鎖策略去保證資源能被正確的使用。這裏咱們還缺一個角度,那就是從線程的角度去調度資源。

咱們用一個問題開頭來展開對話。

·問:咱們能根據資源的數量去建立線程的數量嗎?

·答:不能,由於建立線程的開銷大,受機器的配置的限制。

·問:那麼能不能建立必定數量的線程,去循環的調度資源。

·答:這麼作是能夠的,可是資源數通常來講確定是多於線程數,咱們要控制資源的調度順序,還將來得及調度的資源能夠按先來後到原則存放到隊列。

·問:那資源數少於線程數時候,該怎麼樣去處理。

·答:咱們能夠保留必定量的線程,爲將來可能調度的資源作預備。

這就是一個線程池的雛形,線程池的雛形具備如下的基本特性

1. 具備最大的線程數限制

2. 有若干常備線程(核心線程)

3. 資源數若多超過了最大線程數的限制則會放入隊列中。

咱們來解刨線程池的最全的配置信息:

1. corePoolSize:核心線程數

2. maximumPoolSize:最大的線程數

3. keepAliveTime:無資源調度的線程回收的時間(默認單位:毫秒)

4. TimeUnit:時間單位

5. BlockingQueueRunnable:多餘的資源放入的隊列

6. ThreadFactory:線程的工廠類

7. RejectedExecutionHandler:線程池調度資源的策略

按照咱們常規的設置

BlockingQueue maximumPoolSize ≥ corePoolSize

·corePoolSize會隨着資源調度數增長至maximumPoolSize

·當線程空閒時,會根據keepAliveTime來回收線程數(maximumPoolSize-corePoolSize)

·BlockingQueue分無界Queue和有界Queue。

·當資源調度大於maximumPoolSize時會放入BlockingQueue中

o 當BlockingQueue是有界隊列則存入

o 當BlockingQueue是無界隊列則根據策略調整

·當資源調度數大於BlockingQueue的長度,則根據RejectedExecutionHandler的策略來調整資源調度狀況。

o AbortPolicy:默認策略,捨棄最新的資源調度,並拋出異常

o DiscardPolicy:捨棄最新的資源調度,不會有異常

o DiscardOldestPolicy:捨棄在隊列中隊頭的資源

o CallerRunsPolicy:交由主線程去執行(慎用

o 自定義拒絕策略:實現RejectedExecutionHandler接口,編寫特殊業務的拒絕策略。

總結:線程池就是多個線程來調度多個資源時所優化的一種多維度的策略,它的核心就是線程的複用以及資源的緩衝存儲。

相關文章
相關標籤/搜索