進(jìn)程是所有線程的集合,每一個線程是進(jìn)程中的一條執(zhí)行路徑。
比方:通過查看 windows 任務(wù)管理器中的列表,我們可以把運(yùn)行在內(nèi)存中的 exe 文件理解成進(jìn)程,進(jìn)程是受操作系統(tǒng)管理的基本運(yùn)行單元。
主要體現(xiàn)在多線程提高程序效率,但是需要注意,并不是使用了多線程就一定能提升性能,有的情況反而會降低性能。
多線程應(yīng)用場景:
我們知道,在我們單線程中,代碼是順序執(zhí)行的,如果前面的操作發(fā)生了阻塞,那么就會影響到后面的操作,這時候可以采用多線程,可以理解成異步調(diào)用;其實前端里的 ajax 就是一個很好地例子,默認(rèn) ajax 是開啟異步的,調(diào)用時瀏覽器會啟一個新的線程,不阻塞當(dāng)前頁面的正常操作;
以http server為例,如果只用單線程響應(yīng)HTTP請求,即處理完一條請求,再處理下一條請求的話,CPU會存在大量的閑置時間;
因為處理一條請求,經(jīng)常涉及到RPC、數(shù)據(jù)庫訪問、磁盤IO等操作,這些操作的速度比CPU慢很多,而在等待這些響應(yīng)的時候,CPU卻不能去處理新的請求,因此http server的性能就很差;
所以很多web容器,都采用對每個請求創(chuàng)建新線程來響應(yīng)的方式實現(xiàn),這樣在等待請求A的IO操作的等待時間里,就可以去繼續(xù)處理請求B,對并發(fā)的響應(yīng)性就好了很多 。
/** * author: niceyoo * blog: https://cnblogs.com/niceyoo * desc: */public class ThreadDemo { public static void main(String[] args) { System.out.println("-----多線程創(chuàng)建開始-----"); /* 1.創(chuàng)建一個線程*/ CreateThread createThread = new CreateThread(); /* 2.開始執(zhí)行線程 注意 開啟線程不是調(diào)用run方法,而是start方法*/ System.out.println("-----多線程創(chuàng)建啟動-----"); createThread.start(); System.out.println("-----多線程創(chuàng)建結(jié)束-----"); }}class CreateThread extends Thread { /*run方法中編寫 多線程需要執(zhí)行的代碼*/ @Override public void run() { for (int i = 0; i< 10; i ) { System.out.println("i:" i); } }}
打印結(jié)果:
-----多線程創(chuàng)建開始----------多線程創(chuàng)建啟動----------多線程創(chuàng)建結(jié)束-----i:0i:1i:2i:3i:4i:5i:6i:7i:8i:9
線程調(diào)用 start() 方法后,代碼并沒有從上往下執(zhí)行,而是有一條新的執(zhí)行分支。
注意:畫圖演示多線程不同執(zhí)行路徑。
/** * author: niceyoo * blog: https://cnblogs.com/niceyoo * desc: */class CreateRunnable implements Runnable { @Override public void run() { for (int i = 0; i< 10; i ) { System.out.println("i:" i); } }}public class ThreadDemo2 { public static void main(String[] args) { System.out.println("-----多線程創(chuàng)建開始-----"); /* 1.創(chuàng)建一個線程 */ CreateRunnable createThread = new CreateRunnable(); /* 2.開始執(zhí)行線程 注意 開啟線程不是調(diào)用run方法,而是start方法 */ System.out.println("-----多線程創(chuàng)建啟動-----"); Thread thread = new Thread(createThread); thread.start(); System.out.println("-----多線程創(chuàng)建結(jié)束-----"); }}
打印結(jié)果:
-----多線程創(chuàng)建開始----------多線程創(chuàng)建啟動----------多線程創(chuàng)建結(jié)束-----i:0i:1i:2i:3i:4i:5i:6i:7i:8i:9
使用實現(xiàn)Runnable接口好,繼承方式的擴(kuò)展性不強(qiáng),java總只支持單繼承,如果一個類繼承Thread就不能繼承其他的類了。
java 中有兩種線程,一種是用戶線程,一種是守護(hù)線程。
用戶線程:指用戶自定義創(chuàng)建的線程,主線程停止,用戶線程不會停止。
守護(hù)線程:當(dāng)前進(jìn)程不存在或主線程停止,守護(hù)進(jìn)程也會被停止。
只需要調(diào)用 setDaemon(true) 方法即可設(shè)置為守護(hù)線程。
/** * author: niceyoo * blog: https://cnblogs.com/niceyoo * desc: */public class DaemonThread { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(100); } catch (Exception e) { } System.out.println("我是子線程..."); } } }); thread.setDaemon(true); thread.start(); for (int i = 0; i < 10; i ) { try { Thread.sleep(100); } catch (Exception e) { } System.out.println("我是主線程"); } System.out.println("主線程執(zhí)行完畢!"); }}
運(yùn)行結(jié)果:
...我是主線程我是子線程...我是主線程主線程執(zhí)行完畢!
從運(yùn)行結(jié)果看到,main函數(shù)執(zhí)行完了,守護(hù)線程也跟著停止了。
線程從創(chuàng)建、運(yùn)行到結(jié)束,總是處于下面五個狀態(tài)之一:
新建狀態(tài)、就緒狀態(tài)、運(yùn)行狀態(tài)、阻塞狀態(tài)以及死亡狀態(tài)。
當(dāng)用new操作符創(chuàng)建一個線程時, 例如new Thread?,線程還沒有開始運(yùn)行,此時線程處在新建狀態(tài)。 當(dāng)一個線程處于新生狀態(tài)時,程序還沒有開始運(yùn)行線程中的代碼
一個新創(chuàng)建的線程并不自動開始運(yùn)行,要執(zhí)行線程,必須調(diào)用線程的start()方法。當(dāng)線程對象調(diào)用start()方法即啟動了線程,start()方法創(chuàng)建線程運(yùn)行的系統(tǒng)資源,并調(diào)度線程運(yùn)行run()方法。當(dāng)start()方法返回后,線程就處于就緒狀態(tài)。
處于就緒狀態(tài)的線程并不一定立即運(yùn)行run()方法,線程還必須同其他線程競爭CPU時間,只有獲得CPU時間才可以運(yùn)行線程。因為在單CPU的計算機(jī)系統(tǒng)中,不可能同時運(yùn)行多個線程,一個時刻僅有一個線程處于運(yùn)行狀態(tài)。因此此時可能有多個線程處于就緒狀態(tài)。對多個處于就緒狀態(tài)的線程是由Java運(yùn)行時系統(tǒng)的線程調(diào)度程序(thread scheduler)來調(diào)度的。
當(dāng)線程獲得CPU時間后,它才進(jìn)入運(yùn)行狀態(tài),真正開始執(zhí)行run()方法.
線程運(yùn)行過程中,可能由于各種原因進(jìn)入阻塞狀態(tài):
有兩個原因會導(dǎo)致線程死亡:
為了確定線程在當(dāng)前是否存活著(就是要么是可運(yùn)行的,要么是被阻塞了),需要使用 isAlive() 方法。如果是可運(yùn)行或被阻塞,這個方法返回true; 如果線程仍舊是new狀態(tài)且不是可運(yùn)行的, 或者線程死亡了,則返回false.
在多線程中也是有執(zhí)行的優(yōu)先級的,所謂的優(yōu)先級,就是cpu是否格外關(guān)注這位小兄弟,優(yōu)先級越大,自然獲得的好處就越多。
當(dāng)在主線程當(dāng)中執(zhí)行到 小弟.join() 方法時,就認(rèn)為主線程應(yīng)該把執(zhí)行權(quán)讓給 小弟。
舉一個例子:
創(chuàng)建一個線程,如何讓子線程執(zhí)行完畢后,主線程才能執(zhí)行呢?
public class ThreadDemo3 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i ) { try { Thread.sleep(10); } catch (Exception e) { } System.out.println(Thread.currentThread().getName() "i:" i); } } }); t1.start(); /* 當(dāng)在主線程當(dāng)中執(zhí)行到t1.join()方法時,就認(rèn)為主線程應(yīng)該把執(zhí)行權(quán)讓給t1 */ t1.join(); for (int i = 0; i < 10; i ) { try { Thread.sleep(10); } catch (Exception e) { } System.out.println("main" "i:" i); } }}
打印結(jié)果:
Thread-0i:0Thread-0i:1Thread-0i:2Thread-0i:3Thread-0i:4Thread-0i:5Thread-0i:6Thread-0i:7Thread-0i:8Thread-0i:9maini:0maini:1maini:2maini:3maini:4maini:5maini:6maini:7maini:8maini:9
雖然上邊在介紹 join 方法時提到了優(yōu)先級,但是在使用 join() 方法后,該線程卻變成了完全主導(dǎo),這或許并不是你想要的結(jié)果。
現(xiàn)代操作系統(tǒng)基本采用時分的形式調(diào)度運(yùn)行的線程,線程分配得到的時間片的多少決定了線程使用處理器資源的多少,也對應(yīng)了線程優(yōu)先級這個概念。在JAVA線程中,通過一個int priority來控制優(yōu)先級,范圍為1-10,其中10最高,默認(rèn)值為5。下面是源碼(基于1.8)中關(guān)于priority的一些量和方法。
class PrioritytThread implements Runnable { @Override public void run() { for (int i = 0; i < 10; i ) { System.out.println(Thread.currentThread().toString() "---i:" i); } }}public class ThreadDemo4 { public static void main(String[] args) { PrioritytThread prioritytThread = new PrioritytThread(); Thread t1 = new Thread(prioritytThread); Thread t2 = new Thread(prioritytThread); t1.start(); /* 注意設(shè)置了優(yōu)先級, 不代表每次都一定會被執(zhí)行。 只是CPU調(diào)度會有限分配 */ t1.setPriority(10); t2.start(); }}
打印結(jié)果:
Thread[t1,10,main]---i:0Thread[t1,10,main]---i:1Thread[t1,10,main]---i:2Thread[t1,10,main]---i:3Thread[t1,10,main]---i:4Thread[t1,10,main]---i:5Thread[t1,10,main]---i:6Thread[t1,10,main]---i:7Thread[t1,10,main]---i:8Thread[t1,10,main]---i:9Thread[t2,5,main]---i:0Thread[t2,5,main]---i:1Thread[t2,5,main]---i:2Thread[t2,5,main]---i:3Thread[t2,5,main]---i:4Thread[t2,5,main]---i:5Thread[t2,5,main]---i:6Thread[t2,5,main]---i:7Thread[t2,5,main]---i:8Thread[t2,5,main]---i:9
答:進(jìn)程是所有線程的集合,每一個線程是進(jìn)程中的一條執(zhí)行路徑。
答:提高程序效率
答:繼承Thread或Runnable 接口。
答:實現(xiàn)Runnable接口好,繼承方式的擴(kuò)展性不強(qiáng),java總只支持單繼承,如果一個類繼承Thread就不能繼承其他的類了。
答:主要能體現(xiàn)到多線程提高程序效率。
舉例:分批發(fā)送短信。
我們了解了什么是線程,線程是一條執(zhí)行路徑,每個線程互不影響;
了解了什么是多線程,多線程在一個線程中,有多條不同的執(zhí)行路徑,并行執(zhí)行,目的為了提高程序效率。
了解了線程創(chuàng)建常見的兩種方式:繼承Thread類實現(xiàn)run方法,或者實現(xiàn)Runnable接口。
事實上,實際開發(fā)中這兩種方式并不常見,而是使用線程池來進(jìn)行管理。
了解了線程的幾種狀態(tài),新建、就緒、運(yùn)行、阻塞、死亡。
了解了線程里面也是有優(yōu)先級的,用數(shù)值1-10來記錄,默認(rèn)是5,最大是10,通過調(diào)用 setPriority(10) 來設(shè)置優(yōu)先級,需要一提的是,并不是優(yōu)先級越大就一定要先執(zhí)行完,只是優(yōu)先執(zhí)行完的概率要大。
我創(chuàng)建了一個java相關(guān)的公眾號,用來記錄自己的學(xué)習(xí)之路,感興趣的小伙伴可以關(guān)注一下微信公眾號哈:niceyoo