圖文詳解Java線程和線程池
一、什么是線程,線程和進(jìn)程的區(qū)別是什么
程序執(zhí)行流的最小執(zhí)行單位,是行程中的實際運作單位,經(jīng)常容易和進(jìn)程這個概念混淆。那么,線程和進(jìn)程究竟有什么區(qū)別呢?首先,進(jìn)程是一個動態(tài)的過程,是一個活動的實體。簡單來說,一個應(yīng)用程序的運行就可以被看做是一個進(jìn)程,而線程,是運行中的實際的任務(wù)執(zhí)行者??梢哉f,進(jìn)程中包含了多個可以同時運行的線程。
二、線程中的基本概念,線程的生命周期
第一步,是用new Thread()的方法新建一個線程,在線程創(chuàng)建完成之后,線程就進(jìn)入了就緒(Runnable)狀態(tài),此時創(chuàng)建出來的線程進(jìn)入搶占CPU資源的狀態(tài),當(dāng)線程搶到了CPU的執(zhí)行權(quán)之后,線程就進(jìn)入了運行狀態(tài)(Running),當(dāng)該線程的任務(wù)執(zhí)行完成之后或者是非常態(tài)的調(diào)用的stop()方法之后,線程就進(jìn)入了死亡狀態(tài)。而我們在圖解中可以看出,線程還具有一個則色的過程,這是怎么回事呢?當(dāng)面對以下幾種情況的時候,容易造成線程阻塞,第一種,當(dāng)線程主動調(diào)用了sleep()方法時,線程會進(jìn)入則阻塞狀態(tài),除此之外,當(dāng)線程中主動調(diào)用了阻塞時的IO方法時,這個方法有一個返回參數(shù),當(dāng)參數(shù)返回之前,線程也會進(jìn)入阻塞狀態(tài),還有一種情況,當(dāng)線程進(jìn)入正在等待某個通知時,會進(jìn)入阻塞狀態(tài)。那么,為什么會有阻塞狀態(tài)出現(xiàn)呢?我們都知道,CPU的資源是十分寶貴的,所以,當(dāng)線程正在進(jìn)行某種不確定時長的任務(wù)時,Java就會收回CPU的執(zhí)行權(quán),從而合理應(yīng)用CPU的資源。我們根據(jù)圖可以看出,線程在阻塞過程結(jié)束之后,會重新進(jìn)入就緒狀態(tài),重新?lián)寠ZCPU資源。這時候,我們可能會產(chǎn)生一個疑問,如何跳出阻塞過程呢?又以上幾種可能造成線程阻塞的情況來看,都是存在一個時間限制的,當(dāng)sleep()方法的睡眠時長過去后,線程就自動跳出了阻塞狀態(tài),第二種則是在返回了一個參數(shù)之后,在獲取到了等待的通知時,就自動跳出了線程的阻塞過程
三、單線程和多線程
單線程,顧名思義即是只有一條線程在執(zhí)行任務(wù),這種情況在我們?nèi)粘5墓ぷ鲗W(xué)習(xí)中很少遇到,所以我們只是簡單做一下了解
多線程,創(chuàng)建多條線程同時執(zhí)行任務(wù),這種方式在我們的日常生活中比較常見。但是,在多線程的使用過程中,還有許多需要我們了解的概念。比如,在理解上并行和并發(fā)的區(qū)別,以及在實際應(yīng)用的過程中多線程的安全問題,對此,我們需要進(jìn)行詳細(xì)的了解。
并行和并發(fā):在我們看來,都是可以同時執(zhí)行多種任務(wù),那么,到底他們二者有什么區(qū)別呢?
并發(fā),從宏觀方面來說,并發(fā)就是同時進(jìn)行多種時間,實際上,這幾種時間,并不是同時進(jìn)行的,而是交替進(jìn)行的,而由于CPU的運算速度非常的快,會造成我們的一種錯覺,就是在同一時間內(nèi)進(jìn)行了多種事情
而并發(fā),則是真正意義上的同時進(jìn)行多種事情。這種只可以在多核CPU的基礎(chǔ)下完成。
還有就是多線程的安全問題?為什么會造成多線程的安全問題呢?我們可以想象一下,如果多個線程同時執(zhí)行一個任務(wù),name意味著他們共享同一種資源,由于線程CPU的資源不一定可以被誰搶占到,這是,第一條線程先搶占到CPU資源,他剛剛進(jìn)行了第一次操作,而此時第二條線程搶占到了CPU的資源,name,共享資源還來不及發(fā)生變化,就同時有兩條數(shù)據(jù)使用了同一條資源,具體請參考多線程買票問題。這個問題我們應(yīng)該如何解決那?
有造成問題的原因我們可以看出,這個問題主要的矛盾在于,CPU的使用權(quán)搶占和資源的共享發(fā)生了沖突,解決時,我們只需要讓一條線程戰(zhàn)歌了CPU的資源時,阻止第二條線程同時搶占CPU的執(zhí)行權(quán),在代碼中,我們只需要在方法中使用同步代碼塊即可。在這里,同步代碼塊不多進(jìn)行贅述,可以自行了解。
四,線程池的原理解析
又以上介紹我們可以看出,在一個應(yīng)用程序中,我們需要多次使用線程,也就意味著,我們需要多次創(chuàng)建并銷毀線程。而創(chuàng)建并銷毀線程的過程勢必會消耗內(nèi)存。而在Java中,內(nèi)存資源是及其寶貴的,所以,我們就提出了線程池的概念。
線程池:Java中開辟出了一種管理線程的概念,這個概念叫做線程池,從概念以及應(yīng)用場景中,我們可以看出,線程池的好處,就是可以方便的管理線程,也可以減少內(nèi)存的消耗。
那么,我們應(yīng)該如何創(chuàng)建一個線程池那?Java中已經(jīng)提供了創(chuàng)建線程池的一個類:Executor
而我們創(chuàng)建時,一般使用它的子類:ThreadPoolExecutor.
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
這是其中最重要的一個構(gòu)造方法,這個方法決定了創(chuàng)建出來的線程池的各種屬性,下面依靠一張圖來更好的理解線程池和這幾個參數(shù):
又圖中,我們可以看出,線程池中的corePoolSize就是線程池中的核心線程數(shù)量,這幾個核心線程,只是在沒有用的時候,也不會被回收,maximumPoolSize就是線程池中可以容納的最大線程的數(shù)量,而keepAliveTime,就是線程池中除了核心線程之外的其他的最長可以保留的時間,因為在線程池中,除了核心線程即使在無任務(wù)的情況下也不能被清除,其余的都是有存活時間的,意思就是非核心線程可以保留的最長的空閑時間,而util,就是計算這個時間的一個單位,workQueue,就是等待隊列,任務(wù)可以儲存在任務(wù)隊列中等待被執(zhí)行,執(zhí)行的是FIFIO原則(先進(jìn)先出)。threadFactory,就是創(chuàng)建線程的線程工廠,最后一個handler,是一種拒絕策略,我們可以在任務(wù)滿了知乎,拒絕執(zhí)行某些任務(wù)。
線程池的執(zhí)行流程又是怎樣的呢?
有圖我們可以看出,任務(wù)進(jìn)來時,首先執(zhí)行判斷,判斷核心線程是否處于空閑狀態(tài),如果不是,核心線程就先就執(zhí)行任務(wù),如果核心線程已滿,則判斷任務(wù)隊列是否有地方存放該任務(wù),若果有,就將任務(wù)保存在任務(wù)隊列中,等待執(zhí)行,如果滿了,在判斷最大可容納的線程數(shù),如果沒有超出這個數(shù)量,就開創(chuàng)非核心線程執(zhí)行任務(wù),如果超出了,就調(diào)用handler實現(xiàn)拒絕策略。
handler的拒絕策略有四種:
AbortPolicy
:不執(zhí)行新任務(wù),直接拋出異常,提示線程池已滿DisCardPolicy
:不執(zhí)行新任務(wù),也不拋出異常
DisCardOldSetPolicy
:將消息隊列中的第一個任務(wù)替換為當(dāng)前新進(jìn)來的任務(wù)執(zhí)行
CallerRunsPolicy
:直接調(diào)用execute來執(zhí)行當(dāng)前任務(wù)
五,常見的幾種線程池的特點以及各自的應(yīng)用場景
CachedThreadPool
:可緩存的線程池,該線程池中沒有核心線程,非核心線程的數(shù)量為Integer.max_value,就是無限大,當(dāng)有需要時創(chuàng)建線程來執(zhí)行任務(wù),沒有需要時回收線程,適用于耗時少,任務(wù)量大的情況。
SecudleThreadPool
:周期性執(zhí)行任務(wù)的線程池,按照某種特定的計劃執(zhí)行線程中的任務(wù),有核心線程,但也有非核心線程,非核心線程的大小也為無限大。適用于執(zhí)行周期性的任務(wù)。
SingleThreadPool
:只有一條線程來執(zhí)行任務(wù),適用于有順序的任務(wù)的應(yīng)用場景。
FixedThreadPool
:定長的線程池,有核心線程,核心線程的即為最大的線程數(shù)量,沒有非核心線程
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
mybatis plus CU自動填充 和 軟刪除自動填充的實現(xiàn)方法
這篇文章主要介紹了mybatis plus CU自動填充 和 軟刪除自動填充的實現(xiàn)方法,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-07-07Java使用數(shù)組實現(xiàn)ArrayList的動態(tài)擴(kuò)容的方法
這篇文章主要介紹了Java使用數(shù)組實現(xiàn)ArrayList的動態(tài)擴(kuò)容的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06Java swing實現(xiàn)支持錄音等功能的鋼琴程序
這篇文章主要為大家詳細(xì)介紹了Java swing實現(xiàn)鋼琴程序,支持錄音等功能的Java鋼琴源碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06Retrofit+RxJava實現(xiàn)帶進(jìn)度條的文件下載
這篇文章主要為大家詳細(xì)介紹了Retrofit+RxJava實現(xiàn)帶進(jìn)度條的文件下載,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-06-06