Java的面向?qū)ο缶幊?/a>">Java的面向?qū)ο缶幊?/a>
723
2022-05-30
1、線程是什么
線程是操作系統(tǒng)調(diào)度的最小單元,也叫輕量級(jí)進(jìn)程。它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。同一進(jìn)程可以創(chuàng)建多個(gè)線程,每個(gè)進(jìn)程都有自己獨(dú)立的一塊內(nèi)存空間。并且能夠訪問共享的內(nèi)存變量。
總之:線程就是一個(gè)電腦的工作單位,可以直接類比現(xiàn)實(shí)生活中的一個(gè)"勞動(dòng)力"(一個(gè)人)
舉個(gè)例子,開了一家餐廳,餐廳這個(gè)實(shí)體就是進(jìn)程,餐廳里的服務(wù)員就是線程,餐廳里的座位就是資源(游戲內(nèi)的數(shù)據(jù)),所有的服務(wù)員都可以安排客人就座,多個(gè)服務(wù)員安排座位就是多線程競(jìng)爭(zhēng),鎖也就是去排號(hào)。線程池就是有多個(gè)服務(wù)員一直站在那里等著被呼叫。
進(jìn)程和線程的區(qū)別可以通俗理解為進(jìn)程是一個(gè)公司,而線程是公司里的工作人員,真正干活的還是個(gè)人
2、啟動(dòng)線程
java創(chuàng)建線程的三種方式:
*繼承Thread類創(chuàng)建線程類*,無(wú)法繼承其他類。
*實(shí)現(xiàn)Runnable接口*
*通過Callable和Future創(chuàng)建線程*
package thread; /** * @author 香菜 */ public class ExtendThread extends Thread { @Override public void run() { System.out.println("ExtendThread");; } } package thread; import java.util.concurrent.Callable; /** * @author 香菜 */ public class ImpCallable implements Callable
總結(jié):線程的概念來(lái)自于生活,理解了概念,在項(xiàng)目中思考的時(shí)候只要搞清楚項(xiàng)目的線程模型,基本上不會(huì)遇到太大的問題
3、線程池
線程池存在的原因是為了節(jié)省創(chuàng)建線程和銷毀線程的系統(tǒng)損耗,這樣說可能不太好理解,我們直接通俗點(diǎn)解釋。
我們做了一個(gè)飯館,大家都知道飯館的營(yíng)業(yè)時(shí)間是有周期性的,也就是飯點(diǎn)的時(shí)候客人才多,在其他的時(shí)間飯店里肯定是沒有人的,我們?cè)趺礃庸腿藥兔龋?/p>
假如我們?cè)诳吹娇腿硕嗟臅r(shí)候感覺招募兩個(gè)店員,然后開始讓他們進(jìn)行干活,飯點(diǎn)過了就開掉,這樣的話就是開啟一個(gè)線程,沒事做了趕緊銷毀掉。這樣的處理邏輯明顯不符合飯館的操作,大家都知道每個(gè)飯館的工作人員基本上都是固定的,為什么這樣吶?首先開啟線程,也就是招聘會(huì)有開銷,比如發(fā)廣告,面試,這些都很費(fèi)時(shí)間,而且招到以后還要培訓(xùn),如果用完之后直接開掉,肯定是不合適的,所以這個(gè)時(shí)候我們需要一些長(zhǎng)期的工作人員維持在店里,也就是線程池了。
線程池的原理是同樣的:保留一部分的線程在系統(tǒng)內(nèi),避免線程的創(chuàng)建和銷毀的系統(tǒng)消耗,隨取隨用。
4、線程池的創(chuàng)建
java中創(chuàng)建線程池的方式一般有兩種:
通過Executors工廠方法創(chuàng)建
通過new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue
通過Executors工廠方法創(chuàng)建
Executor 提供一種將任務(wù)提交與每個(gè)任務(wù)將如何運(yùn)行的機(jī)制(包括線程使用的細(xì)節(jié)、調(diào)度等)分離開來(lái)的方法。
相當(dāng)于manager,老板讓manager去執(zhí)行一件任務(wù),具體的是誰(shuí)執(zhí)行,什么時(shí)候執(zhí)行,就不管了。
介紹幾個(gè)
內(nèi)置的線程池基本上都在這里
newScheduledThreadPool 定時(shí)執(zhí)行的線程池
newCachedThreadPool 緩存使用過的線程
newFixedThreadPool 固定數(shù)量的線程池
newWorkStealingPool 將大任務(wù)分解為小任務(wù)的線程池
通過構(gòu)造函數(shù)創(chuàng)建
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) ;
int corePoolSize :表示線程池的數(shù)量,常態(tài)下的線程數(shù)量
int maximumPoolSize :表示線程池最大的線程池?cái)?shù)量,極限情況下的最大數(shù)量
long keepAliveTime :表示線程沒有任務(wù)執(zhí)行時(shí)最多保持多久時(shí)間會(huì)終止。默認(rèn)情況下,只有當(dāng)線程池中的線程數(shù)大于corePoolSize時(shí),keepAliveTime才會(huì)起作用,直到線程池中的線程數(shù)不大于corePoolSize,即當(dāng)線程池中的線程數(shù)大于corePoolSize時(shí),如果一個(gè)線程空閑的時(shí)間達(dá)到keepAliveTime,則會(huì)終止,直到線程池中的線程數(shù)不超過corePoolSize。但是如果調(diào)用了
TimeUnit unit :參數(shù)keepAliveTime的時(shí)間單位,
BlockingQueue
常見選擇有:
ArrayBlockingQueue 少用 LinkedBlockingQueue 常用 SynchronousQueue 常用 PriorityBlockingQueue 少用
ThreadFactory threadFactory :用于設(shè)置創(chuàng)建線程的工廠,可以設(shè)置線程的名字和優(yōu)先級(jí)等等
RejectedExecutionHandler handler:看類名也能猜到是拒絕處理任務(wù)時(shí)的策略。主要有下面幾種可以選擇
1、AbortPolicy:直接拋出異常。
2、CallerRunsPolicy:只用調(diào)用者所在線程來(lái)運(yùn)行任務(wù)。
3、DiscardOldestPolicy:丟棄隊(duì)列里最近的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù)。
4、DiscardPolicy:不處理,丟棄掉。
5、也可以根據(jù)應(yīng)用場(chǎng)景需要來(lái)實(shí)現(xiàn)RejectedExecutionHandler接口自定義策略。如記錄日志或持久化不能處理的任務(wù)。
5、調(diào)試線程
多線程的調(diào)試可能是一些同學(xué)不太會(huì),大概說下怎么回事
下面創(chuàng)建了一個(gè)10個(gè)線程的線程池,并提交了3個(gè)任務(wù),運(yùn)行的時(shí)候生成了三個(gè)線程創(chuàng)建了一個(gè)3個(gè)線程的線程池,提交了3個(gè)任務(wù),運(yùn)行的時(shí)候生成了三個(gè)線程
ExecutorService executorService = Executors.newFixedThreadPool(3); executorService.submit(()-> System.out.println("1111111111111111111111")); executorService.submit(()-> System.out.println("222222222222222222")); executorService.submit(()-> System.out.println("3333333333333333333333"));
添加斷點(diǎn):
添加斷點(diǎn)應(yīng)該每個(gè)同學(xué)都會(huì),就是在調(diào)試的時(shí)候需要注意下面兩個(gè)選項(xiàng)
All 就是在斷點(diǎn)發(fā)生的時(shí)候,會(huì)將整個(gè)虛擬機(jī)停住,也就是所有的線程都會(huì)暫停
Thread 就是斷住當(dāng)前線程,其他的線程不收影響,在調(diào)試多線程的時(shí)候一定要選擇這個(gè),測(cè)試多個(gè)線程的并行
6、synchronized關(guān)鍵字
每個(gè)java對(duì)象頭中都有鎖狀態(tài)位標(biāo)記。java中在使用synchronize同步的時(shí)候,肯定是涉及到某個(gè)對(duì)象的鎖。因此,在考慮同步的時(shí)候,首先要想到是同步的是哪個(gè)對(duì)象的鎖。
每個(gè)對(duì)象都和一個(gè)monitor對(duì)象關(guān)聯(lián),主要用來(lái)控制互斥資源的訪問,如果你想要加鎖必須先獲得monitor的批準(zhǔn),如果現(xiàn)在正有線程訪問,會(huì)把申請(qǐng)的線程加入到等待隊(duì)列。在對(duì)臨界資源的加鎖的時(shí)候會(huì)調(diào)用monitor_enter,離開的時(shí)候會(huì)monitorexit 釋放鎖
1、 無(wú)論synchronized關(guān)鍵字加在方法上還是對(duì)象上,如果它作用的對(duì)象是非靜態(tài)的,則它取得的鎖是對(duì)象;如果synchronized作用的對(duì)象是一個(gè)靜態(tài)方法或一個(gè)類,則它取得的鎖是對(duì)class對(duì)象的鎖,該類所有的對(duì)象同一把鎖。
2、每個(gè)對(duì)象只有一個(gè)鎖(lock)與之相關(guān)聯(lián),誰(shuí)拿到這個(gè)鎖誰(shuí)就可以運(yùn)行它所控制的那段代碼。
3、實(shí)現(xiàn)同步是要很大的系統(tǒng)開銷作為代價(jià)的,甚至可能造成死鎖,所以盡量避免無(wú)謂的同步控制,避免做嵌套synchronized 的使用。
4、synchronized 要盡量控制范圍,不能范圍太大,否則會(huì)損失系統(tǒng)性能。
Java 任務(wù)調(diào)度 多線程
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。