java中的多線程是非常重要的一個(gè)知識點(diǎn),下面我們就來簡單的介紹下多線程的相關(guān)知識以及相關(guān)方法裳扯。
并發(fā)與并行
并行就是兩個(gè)任務(wù)同時(shí)運(yùn)行,就是甲任務(wù)進(jìn)行的同時(shí)浊伙,乙任務(wù)也在進(jìn)行。(需要多核CPU)
并發(fā)是指兩個(gè)任務(wù)都請求運(yùn)行长捧,而處理器只能按受一個(gè)任務(wù)嚣鄙,就把這兩個(gè)任務(wù)安排輪流進(jìn)行,由于間時(shí)間隔較短串结,使人感覺兩個(gè)任務(wù)都在運(yùn)行哑子。
線程的五種狀態(tài)
新建,就緒,運(yùn)行,阻塞,死亡
JVM啟動(dòng)的是多線程
JVM啟動(dòng)至少啟動(dòng)了垃圾回收線程和主線程,所以是多線程的奉芦。
java中線程的實(shí)現(xiàn)方式
1赵抢,繼承Thread
使用步驟:
1.定義類繼承Thread
2.重寫run方法
3.把新線程要做的事寫在run方法中
4.創(chuàng)建線程對象
5.開啟新線程, 內(nèi)部會(huì)自動(dòng)執(zhí)行run方法
2,實(shí)現(xiàn)Runnable接口
實(shí)現(xiàn)步驟:
1.定義類實(shí)現(xiàn)Runnable接口
2.實(shí)現(xiàn)run方法
3.把新線程要做的事寫在run方法中
4.創(chuàng)建自定義的Runnable的子類對象,創(chuàng)建Thread對象傳入Runnable
5.調(diào)用start()開啟新線程, 內(nèi)部會(huì)自動(dòng)調(diào)用Runnable的run()方法
3声功,通過Callable<V>接口和 FutureTask<V>創(chuàng)建線程
實(shí)現(xiàn)步驟:
1.創(chuàng)建Callable接口的實(shí)現(xiàn)類烦却,設(shè)置V的類型,并實(shí)現(xiàn)call()方法先巴,該call()方法將作為線程執(zhí)行體其爵,并且有返回值,返回值類型為V類型伸蚯。
2.創(chuàng)建Callable實(shí)現(xiàn)類的實(shí)例摩渺,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值剂邮。
3.使用FutureTask對象作為Thread對象的target創(chuàng)建并啟動(dòng)新線程摇幻。(能作為target是因?yàn)镕utureTask實(shí)現(xiàn)了Runnale接口)
4.調(diào)用FutureTask對象的get()方法來獲得子線程執(zhí)行結(jié)束后的返回值
例:
以上三種創(chuàng)建方式的區(qū)別
繼承Thread : 由于子類重寫了Thread類的run(), 當(dāng)調(diào)用start()時(shí)直接找子類的run()方法。
實(shí)現(xiàn)Runnable : 構(gòu)造函數(shù)中傳入了Runnable的引用, 有個(gè)成員變量記住了它, 調(diào)用run()方法時(shí)內(nèi)部判斷成員變量Runnable的引用是否為空挥萌。
通過Callable接口和FutureTask創(chuàng)建線程:FutureTask是Runnbale的實(shí)現(xiàn)類绰姻,所以與Runnable相同,并且也是調(diào)用run方法引瀑,只是FutureTask將run方法重寫了狂芋,在run方法中調(diào)用call方法。
繼承Thread
好處是:可以直接使用Thread類中的方法,代碼簡單憨栽。
弊端是:如果已經(jīng)有了父類,就不能用這種方法帜矾。
實(shí)現(xiàn)Runnable接口
好處是:即使自己定義的線程類有了父類也沒關(guān)系,因?yàn)橛辛烁割愐部梢詫?shí)現(xiàn)接口,代碼更靈活。
弊端是:不能直接使用Thread中的方法,需要先獲取到線程對象后,才能得到Thread的方法,代碼復(fù)雜屑柔。
通過Callable接口和FutureTask創(chuàng)建線程
好處是:即使自己定義的線程類有了父類也沒關(guān)系,因?yàn)橛辛烁割愐部梢詫?shí)現(xiàn)接口,代碼更靈活屡萤。并且call方法有返回值,還能拋出異常锯蛀。
弊端是:不能直接使用Thread中的方法,需要先獲取到線程對象后,才能得到Thread的方法,代碼復(fù)雜相對于實(shí)現(xiàn)Runnable接口的方式更加復(fù)雜灭衷。
獲取線程名字和設(shè)置名字
通過Thread的getName()方法獲取線程對象的名字。
通過setName(String)方法可以設(shè)置線程對象的名字旁涤。
通過構(gòu)造函數(shù)可以傳入String類型的名字翔曲。
每個(gè)線程系統(tǒng)都會(huì)默認(rèn)分配個(gè)名字,主線程:main,子線程thread-0 ....
獲取當(dāng)前線程的對象
Thread.currentThread()方法用于獲取當(dāng)前線程對象迫像。
線程的其它方法
Thread.sleep(毫秒), 控制當(dāng)前線程休眠若干毫秒。
setDaemon(), 設(shè)置一個(gè)線程為守護(hù)線程, 該線程不會(huì)單獨(dú)執(zhí)行, 當(dāng)其他非守護(hù)線程都執(zhí)行結(jié)束后, 自動(dòng)退出瞳遍。特點(diǎn):男守護(hù)女闻妓,女的死,男的也不想活了掠械。
join(), 當(dāng)前線程暫停, 等待指定的線程執(zhí)行結(jié)束后,當(dāng)前線程再繼續(xù)由缆,記住哪個(gè)線程調(diào)用該方法,則先執(zhí)行哪個(gè)線程猾蒂。
join(int), 可以等待指定的毫秒之后繼續(xù)均唉。
join方法詳解:join方法的原理就是調(diào)用相應(yīng)線程的wait方法進(jìn)行等待操作的,例如A線程中調(diào)用了B線程的join方法肚菠,則相當(dāng)于在A線程中調(diào)用了B線程的wait方法(這里是將B線程對象設(shè)為A線程在等待池中的標(biāo)識)舔箭,當(dāng)B線程執(zhí)行完(或者到達(dá)等待時(shí)間),B線程會(huì)自動(dòng)調(diào)用自身的notifyAll方法(通過標(biāo)識可以在等待池中找到A線程然后喚醒)喚醒A線程蚊逢,從而達(dá)到同步的目的层扶。
yield() 線程讓出cpu。
setPriority()設(shè)置線程的優(yōu)先級烙荷,默認(rèn)優(yōu)先級是5镜会,最小優(yōu)先級1,最高優(yōu)先級10终抽,可以設(shè)置2戳表,3,4昼伴,Thread里面有靜態(tài)常量扒袖。
interrupt(),中斷線程亩码。
interrupt方法詳解:Thread.interrupt 的作用不是中斷線程,而是通知線程應(yīng)該中斷了野瘦,
如果線程處于被阻塞狀態(tài)(例如處于sleep, wait, join 等狀態(tài))描沟,那么線程將立即退出被阻塞狀態(tài),并拋出一個(gè)InterruptedException異常鞭光。此時(shí)我們可以在catch中關(guān)閉線程吏廉。
如果線程處于正常活動(dòng)狀態(tài)惰许,那么會(huì)將該線程的中斷標(biāo)志設(shè)置為 true席覆。被設(shè)置中斷標(biāo)志的線程將繼續(xù)正常運(yùn)行,不受影響汹买。這種情況下佩伤,如果我們需要關(guān)閉線程聊倔,我們可以通過Thread.currentThread.isInterrupted()來判斷中斷標(biāo)志是否為true,如果是生巡,那么我們可以進(jìn)行關(guān)閉線程處理耙蔑,如下圖所示,當(dāng)中斷標(biāo)志為true時(shí)孤荣,循環(huán)結(jié)束甸陌,run方法執(zhí)行完畢。
new Thread(new Runnable() {
public void run() {
while(!Thread.currentThread().isInterrupted()) {
System.out.println("線程的中斷標(biāo)志變?yōu)閒alse時(shí)盐股,執(zhí)行我");
}
System.out.println("線程的中斷標(biāo)志變?yōu)閠rue時(shí)钱豁,執(zhí)行我");
}
}).start();
要記住的是,isInterrupted()返回中斷標(biāo)志后疯汁,會(huì)將中斷標(biāo)志設(shè)為false牲尺。
關(guān)閉線程的幾種方式
1.設(shè)置退出標(biāo)志,使線程正常退出涛目,也就是當(dāng)run()方法完成后線程終止秸谢,如果是循環(huán),那么我們可以設(shè)置當(dāng)不符合循環(huán)條件時(shí)跳出循環(huán)霹肝,或者在循環(huán)代碼中用if判斷條件估蹄,不符合則通過break退出循環(huán)。
2.使用interrupt()方法中斷線程沫换,至于如何關(guān)閉上面已經(jīng)說明臭蚁。
3.使用stop方法強(qiáng)行終止線程(不推薦使用,Thread.stop, Thread.suspend, Thread.resume 和Runtime.runFinalizersOnExit 這些終止線程運(yùn)行的方法已經(jīng)被廢棄讯赏,使用它們是極端不安全的?宥摇)。