嘉興手機網站開發(fā)費用b站2023年免費入口
一、概念介紹
什么是線程,什么是進程,兩者有什么關系?
????????進程是操作系統(tǒng)資源分配的獨立單位;而線程是操作系統(tǒng)能夠進行調度和分派的最小單位;線程包含于進程之中,是進程中的實際運作單位。
例如:
正在運行的360安全衛(wèi)士就屬于一個進程
使用360衛(wèi)士中的各個功能時,就屬于開啟一個線程
1.Thread類與Runnable接口
為什么有了Thread類還要有Runnable接口,兩者各有什么用途?
????????他們兩個最主要的區(qū)別:一個是接口一個是實現(xiàn)類,常用接口可以避免單繼承的局限性外,具體區(qū)別是:繼承Thread類的方式可能會導致類的局部變量不能正確的被共享。因為每個線程都是一個獨立的對象,它們之間不能共享實例變量,如果需要共享變量,就必須使用靜態(tài)變量或共享對象鎖。而使用Runnable接口的方式,多個線程可以共享同一個Runnable實例,從而共享實例變量。
????????使用Runnable接口可以更好的體現(xiàn)面向對象編程的思想,把任務和線程分離開來??梢园讶蝿债斪饕粋€對象,而線程可以看作是該對象(任務)的執(zhí)行者。這樣就可以更好的實現(xiàn)模塊化設計,提高代碼的可重用性和可維護性。
????????使用Runnable接口可以更好地處理多個線程之間的交互和協(xié)作。因為實現(xiàn)了Runnable接口的實例可以作為參數(shù)傳遞給Thread類的構造函數(shù),因此線程(一個Thread實例)可以共享同一個Runnable實例,并且多個線程可以同時執(zhí)行同一個Runnable實例的不同方法,從而實現(xiàn)多個線程之間的交互和協(xié)作。總的來說,使用Runnable接口的方式更加靈活和通用,可以實現(xiàn)更多的多線程編程場景,更好的體現(xiàn)面向對象編程的思想,提高代碼的可重用性和可維護性。
????????但是,使用Thread類的方式也有其適用的場景,例如在一些簡單的多線程編程場景中可以使用繼承Thread類的方式來實現(xiàn)。
2.并發(fā)和并行的區(qū)別
(1).處理任務不同
????????并發(fā)(Concurrent)是一個CPU同時處理多個線程任務。(宏觀上是同時處理多個任務,微觀上其實是CPU在多個線程之間快速的交替執(zhí)行。操作系統(tǒng)中有一個組件叫做任務調度器,它將CPU的時間片分配給各個線程使用,在一個時間段的線程運行時,其他線程處于掛起狀態(tài),這種就稱之為并發(fā)。
????????并行(parallel)是多個CPU處理器同時處理多個線程任務。(當一個CPU執(zhí)行一個線程時,另一個CPU可以執(zhí)行另一個線程,兩個線程互不搶占CPU資源,可以同時進行,這就被稱之為并行。)
(2).CPU資源不同
????????并發(fā)過程中,線程之間會去搶占CPU資源,輪流便用。并行過程中,線程間不會搶占CPU資源。(因為是多個CPU處理器,各做各的。)
3.什么是線程池,使用線程池有哪些優(yōu)勢?
? ? ? ? 什么是線程池?
????????線程池,是一種線程使用模式,線程池維護著多個線程,等待著管理者為其分配可并發(fā)執(zhí)行的任務,就是可管理、維護和分配線程的“池子”。
? ? ? ? 為什么使用線程池?
????????為了減少創(chuàng)建和銷毀線程的次數(shù),讓每個線程郜可以多次的使用;
????????利用線程池可以根據系統(tǒng)情況調整線程的數(shù)量,防止消耗過多內存。在實際使用中,服務器在創(chuàng)建和銷毀線程上花費的時間和系統(tǒng)資源都相當大,使用線程池就可以優(yōu)化這些消耗。
????????通俗的說:使用線程池就是為了讓線程對象可以反復的復用,不需要每次執(zhí)行任務時構建一個新的線程,等到任務處理完后再銷毀。
(1)使用線程池的優(yōu)勢:
(2)線程池的應用場景
二、Java內置線程池的使用方法
(一)線程池類:ThreadPoolExecutor的介紹
????????我們要想自定義線程池,必須先了解線程池的工作原理,才能自己定義線程池 這里我們通過觀察java中ThreadPoolExecutor的源碼來學習線程池的原理 (源碼演示在idea中查看)
1.ThreadPoolExecutor的構造方法(了解這七個核心參數(shù))
public ThreadPoolExecutor(int corePoolSize, //核心線程數(shù)量int maximumPoolSize, //最大線程數(shù)long keepAliveTime, //最大空閑時間TimeUnit unit, ? ? ?//時間單位BlockingQueue<Runnable> workQueue,//任務隊列ThreadFactory threadFactory, ? ? ?//線程工廠RejectedExecutionHandler handler //飽和處理機制)
-
int corePoolSize:核心線程數(shù)量
-
int maximumPoolSize:最大線程數(shù)
-
long keepAliveTime:最大空閑時間,當線程空閑的時長達到該時間時,會被自動回收。
-
TimeUnit unit:設置最大空閑時間的時間單位
-
BlockingQueue<Runnable>workQueue:任務隊列,當線程池中所有線程都不空閑時,線程池在收到任務時,會在該隊列中等候;
-
ThreadFactory threadFactory:線程工廠
-
RejectedExecutionHandler handler:飽和處理機制,當線程池中的線程已達到最大線程數(shù)量,且都在工作;并且任務隊列上也已經排滿等候的任務時,證明當前線程池已經飽和,此時再來任務將會觸發(fā)該機制。
2.線程池的工作流程
我們通過下面一個場景來理解ThreadPoolExecutor中的各個參數(shù),以及線程池的工作流程:
????????a客戶(任務)去銀行(線程池)辦理業(yè)務,但銀行剛開始營業(yè),窗口服務員還未就位(相當于線程池中初始線程數(shù)量為0),于是經理(線程池管理者)就安排1號工作人員(創(chuàng)建1號線程執(zhí)行任務)接待a客戶(創(chuàng)建線程);
????????在a客戶業(yè)務還沒辦完時,b客戶(任務)又來了,于是經理(線程池管理者)就安排2號工作人員(創(chuàng)建2號線程執(zhí)行任務)接待b客戶(又創(chuàng)建了一個新的線程) 假設該銀行總共就2個窗口(核心線程數(shù)量是2);
????????緊接著在a,b客戶都沒有結束的情況下客戶來了,于是經理(線程池管理者)就安排c客戶先坐到銀行大廳的座位上(空位相當于是任務隊列)等候,并告知他: 如果1、2號工作人員空出,c就可以前去辦理業(yè)務;
????????此時d客戶又到了銀行,(工作人員都在忙,大廳座位也滿了)于是經理趕緊安排臨時工(新創(chuàng)建一個線程)在大堂站著,手持pad設備給d客戶辦理業(yè)務;
????????假如前面的業(yè)務都沒有結束的時候e客戶又來了,此時正式工作人員都上了,臨時工也上了,座位也滿了(臨時工加正式員工的總數(shù)量就是最大線程數(shù)),于是經理只能按《超出銀行最大接待能力處理辦法》(飽和處理機制)拒接接待e客戶;
????????最后,進來辦業(yè)務的人少了,大廳的臨時工空閑時間也超過了1個小時(最大空閑時間),經理就會讓這部分空閑的臨時員工下班。(銷毀線程)
????????但是為了保證銀行正常工作(有一個alowCoreThreadTimeout變量控制是否允許銷毀核心線程,默認false),即使正式員工閑著,也不得提前下班,所以1、2號工作人員繼續(xù)待著(池內保持核心線程數(shù)量);
3. 線程池的4個參數(shù)的設計:
(1)核心線程數(shù)(corePoolSize)
核心線程數(shù)的設計需要依據任務的處理時間和每秒產生的任務數(shù)量來確定,例如:
執(zhí)行一個任務需要0.1秒,系統(tǒng)百分之80的時間每秒都會產生100個任務,那么要想在1秒內處理完這100個任務,就需要10個線程,此時我們就可以設計核心線程數(shù)為10;當然實際情況不可能這么平均,所以我們一般按照8020原則設計即可,既按照百分之80的情況設計核心線程數(shù),剩下的百分之20可以利用最大線程數(shù)處理。
(2)任務隊列長度(workQueue)
任務隊列長度一般設計為:核心線程數(shù)/單個任務執(zhí)行時間*2即可;例如上面的場景中,核心線程數(shù)設計為10,單個任務執(zhí)行時間為 0.1秒,則隊列長度可以設計為200。
(3)最大線程數(shù)(maximumPoolSize)
最大線程數(shù)的設計除了需要參照核心線程數(shù)的條件外,還需要參照系統(tǒng)每秒產生的最大任務數(shù)決定:例如:上述環(huán)境中,如果系統(tǒng)每秒最大產生的任務是1000個。那么,最大線程數(shù)=(最大任務數(shù)-任務隊列長度)單個任務執(zhí)行時間;既:最大線程數(shù)=(1000-200)0.1=80個。
(4)最大空閑時間(keepAliveTime)
這個參數(shù)的設計完全參考系統(tǒng)運行環(huán)境和硬件壓力設定,沒有固定的參考值,用戶可以根據經驗和系統(tǒng)產生任務的時間間隔合理設置一個值即可。
注意:上面4個參數(shù)的設置只是一般的設計原則,并不是固定的,用戶也可以根據實際情況靈活調整!
4.線程池類:ThreadPoolExecutor的實際使用
1.自定義線程池-實現(xiàn)步驟
-
(1)編寫任務類(MyTask),實現(xiàn)Runnable接口;
-
(2)編寫線程類(MyWorker),用于執(zhí)行任務,需要持有所有任務:
-
(3)編寫線程池類(MyThreadPool),包含提交任務,執(zhí)行任務的能力;
-
(4)編寫測試類(MyTest),創(chuàng)建線程池對象,提交多個任務測試; (具體代碼參考idea)
小提示:關于線程池的功能比較繁多,這里僅僅模擬了核心功能,其他功能大家可以自行考補全;