[TOC]
1.基本概念
1.1進(jìn)程
操作系統(tǒng)中的獨(dú)立運(yùn)行的程序,每一個(gè)進(jìn)程執(zhí)行都有一個(gè)執(zhí)行順序捐友。一個(gè)進(jìn)程中可以有多個(gè)線程淫半。
1.2 線程
從進(jìn)程創(chuàng)建的,只是進(jìn)程的一部分代碼可以脫離出來(lái)匣砖,與主線程同時(shí)進(jìn)行科吭。
1.3 多線程
多線程是實(shí)現(xiàn)多任務(wù)的一種方式。
1.4并發(fā)
指兩個(gè)或多個(gè)事件在同一時(shí)間段內(nèi)發(fā)生猴鲫。
1.5并行
指兩個(gè)或多個(gè)事件在同一時(shí)刻發(fā)生(同時(shí)發(fā)生)对人。
并發(fā)的關(guān)鍵是你有處理多個(gè)任務(wù)的能力,不一定要同時(shí)拂共。
并行的關(guān)鍵是你有同時(shí)處理多個(gè)任務(wù)的能力牺弄。
2.使用多線程的意義
提高執(zhí)行效率
提高cpu的利用率,在單線程下宜狐,運(yùn)行一個(gè)任務(wù)的時(shí)候势告,第二個(gè)任務(wù)便不能執(zhí)行,而在多線程的下抚恒,主線程執(zhí)行的情況下可以同時(shí)執(zhí)行其他任務(wù)咱台;
系統(tǒng)創(chuàng)建進(jìn)程需要為該進(jìn)程重新分配系統(tǒng)資源,創(chuàng)建線程代價(jià)比較屑笸浴回溺;
線程可以共享數(shù)據(jù),而進(jìn)程之間不能共享數(shù)據(jù)混萝;
Java語(yǔ)言內(nèi)置了多線程功能支持遗遵,簡(jiǎn)化了java多線程編程。
3.線程的狀態(tài)
- 新建 :線程對(duì)象創(chuàng)建譬圣,但是沒(méi)有在其上調(diào)用start()方法瓮恭;
- 就緒(可運(yùn)行) :線程對(duì)象調(diào)用start()方法后雄坪,就處于就緒狀態(tài)厘熟,但調(diào)度程序還沒(méi)有把它選定為運(yùn)行線程時(shí)線程所處的狀態(tài)屯蹦;
- 運(yùn)行 :就緒狀態(tài)下的線程在獲取CPU資源后就可以執(zhí)行run(),此時(shí)的線程便處于運(yùn)行狀態(tài),運(yùn)行狀態(tài)的線程可變?yōu)榫途w绳姨、阻塞及死亡三種狀態(tài)登澜。
- 等待/阻塞/睡眠 :在一個(gè)線程執(zhí)行了sleep(睡眠)、suspend(掛起)等方法后會(huì)失去所占有的資源飘庄,從而進(jìn)入阻塞狀態(tài)脑蠕,其共同點(diǎn)是:線程仍舊是活的,但是當(dāng)前沒(méi)有條件運(yùn)行跪削。換句話說(shuō)谴仙,它是可運(yùn)行的,但是如果某件事件出現(xiàn)碾盐,他可能返回到可運(yùn)行狀態(tài)晃跺。
- 終止 :run()方法完成后或發(fā)生其他終止條件時(shí)就會(huì)切換到終止?fàn)顟B(tài)。
4.創(chuàng)建線程的方法
4.1 繼承Thread類(lèi)
步驟
- 定義類(lèi)繼承Thread毫玖;
- 重寫(xiě)Thread類(lèi)中的run方法掀虎;
目的:將自定義代碼存儲(chǔ)在run方法,讓線程運(yùn)行 - 調(diào)用線程的start方法
啟動(dòng)線程付枫,調(diào)用run方法烹玉。
public class MyThread extends Thread{//繼承Thread類(lèi)
public void run(){
//重寫(xiě)run方法
}
}
public class Main {
public static void main(String[] args){
new MyThread().start();//創(chuàng)建并啟動(dòng)線程
}
}
4.2 實(shí)現(xiàn)Runnable接口
步驟
- 定義子類(lèi),實(shí)現(xiàn)Runnable接口阐滩,并重寫(xiě)run()二打;
- 啟動(dòng)線程,創(chuàng)建Runnable實(shí)現(xiàn)類(lèi)的實(shí)例掂榔,并用這個(gè)實(shí)例作為T(mén)hread的target來(lái)創(chuàng)建Thread對(duì)象址儒;
- 通過(guò)調(diào)用線程對(duì)象的start()方法來(lái)啟動(dòng)線程。
public class MyThread2 implements Runnable {//實(shí)現(xiàn)Runnable接口
public void run(){
//重寫(xiě)run方法
}
}
public class Main {
public static void main(String[] args){
//創(chuàng)建并啟動(dòng)線程
MyThread2 myThread=new MyThread2();
Thread thread=new Thread(myThread);
thread().start();
//或者 new Thread(new MyThread2()).start();
}
}
4.3 使用Callable和Future創(chuàng)建線程
步驟:
創(chuàng)建Callable接口的實(shí)現(xiàn)類(lèi)衅疙,并實(shí)現(xiàn)call()方法莲趣,然后創(chuàng)建該實(shí)現(xiàn)類(lèi)的實(shí)例(從java8開(kāi)始可以直接使用Lambda表達(dá)式創(chuàng)建Callable對(duì)象)。
使用FutureTask類(lèi)來(lái)包裝Callable對(duì)象饱溢,該FutureTask對(duì)象封裝了Callable對(duì)象的call()方法的返回值
使用FutureTask對(duì)象作為T(mén)hread對(duì)象的target創(chuàng)建并啟動(dòng)線程(因?yàn)镕utureTask實(shí)現(xiàn)了Runnable接口)
調(diào)用FutureTask對(duì)象的get()方法來(lái)獲得子線程執(zhí)行結(jié)束后的返回值
public class Main {
public static void main(String[] args){
MyThread3 th=new MyThread3();
//使用Lambda表達(dá)式創(chuàng)建Callable對(duì)象
//使用FutureTask類(lèi)來(lái)包裝Callable對(duì)象
FutureTask<Integer> future=new FutureTask<Integer>(
(Callable<Integer>)()->{
return 5;
}
);
new Thread(task,"有返回值的線程").start();//實(shí)質(zhì)上還是以Callable對(duì)象來(lái)創(chuàng)建并啟動(dòng)線程
try{
System.out.println("子線程的返回值:"+future.get());//get()方法會(huì)阻塞喧伞,直到子線程執(zhí)行結(jié)束才返回
}catch(Exception e){
ex.printStackTrace();
}
}
}
4.4 三種創(chuàng)建方式的對(duì)比
- 采用實(shí)現(xiàn)Runnable、Callable接口的方式創(chuàng)建多線程時(shí)
優(yōu)勢(shì)是:
線程類(lèi)只是實(shí)現(xiàn)了Runnable接口或Callable接口绩郎,還可以繼承其他類(lèi)潘鲫。
在這種方式下,多個(gè)線程可以共享同一個(gè)target對(duì)象肋杖,所以非常適合多個(gè)相同線程來(lái)處理同一份資源的情況溉仑,從而可以將CPU、代碼和數(shù)據(jù)分開(kāi)状植,形成清晰的模型浊竟,較好地體現(xiàn)了面向?qū)ο蟮乃枷搿?/p>
劣勢(shì)是:
編程稍微復(fù)雜怨喘,如果要訪問(wèn)當(dāng)前線程,則必須使用Thread.currentThread()方法振定。
- 使用繼承Thread類(lèi)的方式創(chuàng)建多線程時(shí)必怜,
優(yōu)勢(shì)是:
編寫(xiě)簡(jiǎn)單,如果需要訪問(wèn)當(dāng)前線程后频,則無(wú)需使用Thread.currentThread()方法梳庆,直接使用this即可獲得當(dāng)前線程。
劣勢(shì)是:
線程類(lèi)已經(jīng)繼承了Thread類(lèi)卑惜,所以不能再繼承其他父類(lèi)膏执。
- Runnable和Callable的區(qū)別
(1) Callable規(guī)定(重寫(xiě))的方法是call(),Runnable規(guī)定(重寫(xiě))的方法是run()露久。
(2) Callable的任務(wù)執(zhí)行后可返回值胧后,而Runnable的任務(wù)是不能返回值的。
(3) call方法可以拋出異常抱环,run方法不可以壳快。
(4) 運(yùn)行Callable任務(wù)可以拿到一個(gè)Future對(duì)象,表示異步計(jì)算的結(jié)果镇草。它提供了檢查計(jì)算是否完成的方法眶痰,以等待計(jì)算的完成,并檢索計(jì)算的結(jié)果梯啤。通過(guò)Future對(duì)象可以了解任務(wù)執(zhí)行情況竖伯,可取消任務(wù)的執(zhí)行,還可獲取執(zhí)行結(jié)果因宇。
注:一般推薦采用實(shí)現(xiàn)接口的方式來(lái)創(chuàng)建多線程
參考: