1. 程式人生 > >深入理解線程和線程池

深入理解線程和線程池

https 了解 locking actor 核心線程 分享圖片 即使 card 深入

關於線程和線程池的學習,我們可以從以下幾個方面入手:

第一,什麽是線程,線程和進程的區別是什麽

第二,線程中的基本概念,線程的生命周期

第三,單線程和多線程

第四,線程池的原理解析

第五,常見的幾種線程池的特點以及各自的應用場景

一、

線程,程序執行流的最小執行單位,是行程中的實際運作單位,經常容易和進程這個概念混淆。那麽,線程和進程究竟有什麽區別呢?首先,進程是一個動態的過程,是一個活動的實體。簡單來說,一個應用程序的運行就可以被看做是一個進程,而線程,是運行中的實際的任務執行者。可以說,進程中包含了多個可以同時運行的線程。

二、

線程的生命周期,線程的生命周期可以利用以下的圖解來更好的理解:

技術分享圖片

第一步,是用new Thread()的方法新建一個線程,在線程創建完成之後,線程就進入了就緒(Runnable)狀態,此時創建出來的線程進入搶占CPU資源的狀態,當線程搶到了CPU的執行權之後,線程就進入了運行狀態(Running),當該線程的任務執行完成之後或者是非常態的調用的stop()方法之後,線程就進入了死亡狀態。而我們在圖解中可以看出,線程還具有一個則色的過程,這是怎麽回事呢?當面對以下幾種情況的時候,容易造成線程阻塞,第一種,當線程主動調用了sleep()方法時,線程會進入則阻塞狀態,除此之外,當線程中主動調用了阻塞時的IO方法時,這個方法有一個返回參數,當參數返回之前,線程也會進入阻塞狀態,還有一種情況,當線程進入正在等待某個通知時,會進入阻塞狀態。那麽,為什麽會有阻塞狀態出現呢?我們都知道,CPU的資源是十分寶貴的,所以,當線程正在進行某種不確定時長的任務時,Java就會收回CPU的執行權,從而合理應用CPU的資源。我們根據圖可以看出,線程在阻塞過程結束之後,會重新進入就緒狀態,重新搶奪CPU資源。這時候,我們可能會產生一個疑問,如何跳出阻塞過程呢?又以上幾種可能造成線程阻塞的情況來看,都是存在一個時間限制的,當sleep()方法的睡眠時長過去後,線程就自動跳出了阻塞狀態,第二種則是在返回了一個參數之後,在獲取到了等待的通知時,就自動跳出了線程的阻塞過程

三、

什麽是單線程和多線程?

單線程,顧名思義即是只有一條線程在執行任務,這種情況在我們日常的工作學習中很少遇到,所以我們只是簡單做一下了解

多線程,創建多條線程同時執行任務,這種方式在我們的日常生活中比較常見。但是,在多線程的使用過程中,還有許多需要我們了解的概念。比如,在理解上並行和並發的區別,以及在實際應用的過程中多線程的安全問題,對此,我們需要進行詳細的了解。

並行和並發:在我們看來,都是可以同時執行多種任務,那麽,到底他們二者有什麽區別呢?

並發,從宏觀方面來說,並發就是同時進行多種時間,實際上,這幾種時間,並不是同時進行的,而是交替進行的,而由於CPU的運算速度非常的快,會造成我們的一種錯覺,就是在同一時間內進行了多種事情

而並發,則是真正意義上的同時進行多種事情。這種只可以在多核CPU的基礎下完成。

還有就是多線程的安全問題?為什麽會造成多線程的安全問題呢?我們可以想象一下,如果多個線程同時執行一個任務,name意味著他們共享同一種資源,由於線程CPU的資源不一定可以被誰搶占到,這是,第一條線程先搶占到CPU資源,他剛剛進行了第一次操作,而此時第二條線程搶占到了CPU的資源,name,共享資源還來不及發生變化,就同時有兩條數據使用了同一條資源,具體請參考多線程買票問題。這個問題我們應該如何解決那?

有造成問題的原因我們可以看出,這個問題主要的矛盾在於,CPU的使用權搶占和資源的共享發生了沖突,解決時,我們只需要讓一條線程戰歌了CPU的資源時,阻止第二條線程同時搶占CPU的執行權,在代碼中,我們只需要在方法中使用同步代碼塊即可。在這裏,同步代碼塊不多進行贅述,可以自行了解。

四,線程池

又以上介紹我們可以看出,在一個應用程序中,我們需要多次使用線程,也就意味著,我們需要多次創建並銷毀線程。而創建並銷毀線程的過程勢必會消耗內存。而在Java中,內存資源是及其寶貴的,所以,我們就提出了線程池的概念。

線程池:Java中開辟出了一種管理線程的概念,這個概念叫做線程池,從概念以及應用場景中,我們可以看出,線程池的好處,就是可以方便的管理線程,也可以減少內存的消耗。

那麽,我們應該如何創建一個線程池那?Java中已經提供了創建線程池的一個類:Executor

而我們創建時,一般使用它的子類:ThreadPoolExecutor.

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
這是其中最重要的一個構造方法,這個方法決定了創建出來的線程池的各種屬性,下面依靠一張圖來更好的理解線程池和這幾個參數:

技術分享圖片

又圖中,我們可以看出,線程池中的corePoolSize就是線程池中的核心線程數量,這幾個核心線程,只是在沒有用的時候,也不會被回收,maximumPoolSize就是線程池中可以容納的最大線程的數量,而keepAliveTime,就是線程池中除了核心線程之外的其他的最長可以保留的時間,因為在線程池中,除了核心線程即使在無任務的情況下也不能被清除,其余的都是有存活時間的,意思就是非核心線程可以保留的最長的空閑時間,而util,就是計算這個時間的一個單位,workQueue,就是等待隊列,任務可以儲存在任務隊列中等待被執行,執行的是FIFIO原則(先進先出)。threadFactory,就是創建線程的線程工廠,最後一個handler,是一種拒絕策略,我們可以在任務滿了知乎,拒絕執行某些任務。

線程池的執行流程又是怎樣的呢?

技術分享圖片

有圖我們可以看出,任務進來時,首先執行判斷,判斷核心線程是否處於空閑狀態,如果不是,核心線程就先就執行任務,如果核心線程已滿,則判斷任務隊列是否有地方存放該任務,若果有,就將任務保存在任務隊列中,等待執行,如果滿了,在判斷最大可容納的線程數,如果沒有超出這個數量,就開創非核心線程執行任務,如果超出了,就調用handler實現拒絕策略。

handler的拒絕策略:

有四種:第一種AbortPolicy:不執行新任務,直接拋出異常,提示線程池已滿

第二種DisCardPolicy:不執行新任務,也不拋出異常

第三種DisCardOldSetPolicy:將消息隊列中的第一個任務替換為當前新進來的任務執行

第四種CallerRunsPolicy:直接調用execute來執行當前任務

五,四種常見的線程池:

CachedThreadPool:可緩存的線程池,該線程池中沒有核心線程,非核心線程的數量為Integer.max_value,就是無限大,當有需要時創建線程來執行任務,沒有需要時回收線程,適用於耗時少,任務量大的情況。

SecudleThreadPool:周期性執行任務的線程池,按照某種特定的計劃執行線程中的任務,有核心線程,但也有非核心線程,非核心線程的大小也為無限大。適用於執行周期性的任務。

SingleThreadPool:只有一條線程來執行任務,適用於有順序的任務的應用場景。

FixedThreadPool:定長的線程池,有核心線程,核心線程的即為最大的線程數量,沒有非核心線程
---------------------
原文:https://blog.csdn.net/weixin_40271838/article/details/79998327

深入理解線程和線程池