個(gè)人網(wǎng)站怎樣做超鏈接天堂網(wǎng)
一.JUC并發(fā)編程基礎(chǔ)
1. 并行與并發(fā)
1.1 并發(fā):
- 是在同一實(shí)體上的多個(gè)事件
- 是在一臺(tái)處理器上"同時(shí)處理多個(gè)任務(wù)"
- 同一時(shí)刻,其實(shí)是只有一個(gè)事件在發(fā)生.
即多個(gè)線程搶占同一個(gè)資源.
1.2 并行
- 是在不同實(shí)體上的多個(gè)事件
- 是在多臺(tái)處理器上同時(shí)處理多個(gè)任務(wù)
- 同一時(shí)刻,大家都在做事情.你做你的,我做我的.
即多個(gè)線程同時(shí)執(zhí)行.
2. 進(jìn)程,線程,管程
2.1 進(jìn)程
在系統(tǒng)中運(yùn)行的一個(gè)應(yīng)用程序就是一個(gè)進(jìn)程,每一個(gè)進(jìn)程都有它自己的內(nèi)存空間和系統(tǒng)資源.
2.2 線程
也被叫做輕量級(jí)進(jìn)程,在同一個(gè)進(jìn)程內(nèi)會(huì)有1個(gè)或多個(gè)線程,是大多數(shù)操作系統(tǒng)進(jìn)行時(shí)序調(diào)度的基本單元
2.3 管程
也被稱為Monitor(監(jiān)視器),也就是平時(shí)我們所說(shuō)的鎖.
3. 線程啟動(dòng)的三種方式
3.1 繼承Thread類.重寫run方法
繼承重寫
public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + " " + i);}}}
執(zhí)行
public class ExtendThreadMain {public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start();}}
注意:啟動(dòng)線程是調(diào)用父類的start方法,而不是直接去調(diào)用run方法.
3.2 實(shí)現(xiàn)Runable接口
實(shí)現(xiàn)重寫
public class MyThread implements Runnable{@Overridepublic void run() {for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+":"+i);}}}
主函數(shù)啟動(dòng)線程
public class RunnableThreadMain {public static void main(String[] args) {MyThread myThread = new MyThread();Thread t1 = new Thread(myThread);t1.start();}}
當(dāng)然我們還有另外一種寫法
注意到Thread的構(gòu)造方法的參數(shù)有
里面存在一個(gè)Runnable參數(shù)和一個(gè)線程名的兩個(gè)參數(shù)的方法.熟悉靜態(tài)內(nèi)部類或者Lamba表達(dá)式的應(yīng)該了解,這里完全可以利用這種特性來(lái)啟動(dòng)線程,所以我們可以這樣改寫.
public class RunnableThreadMain {public static void main(String[] args) {// MyThread myThread = new MyThread();// Thread t1 = new Thread(myThread);Thread t1 = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + " " + i);}}, "t1");t1.start();}}
3.3 實(shí)現(xiàn)Callable接口
實(shí)現(xiàn)重寫call方法(其實(shí)也就跟上述兩個(gè)的run方法的作用一樣)
/*** 這里的泛型指的是call方法的返回值*/public class MyThread implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i < 100; i++) {sum+=i;}return sum;}}
主函數(shù)調(diào)度,這里與其他兩種實(shí)現(xiàn)不同的是,可以有返回值,這里引用了FutureTask接口來(lái)接收這個(gè)值
public class CallableThreadMain {public static void main(String[] args) throws ExecutionException, InterruptedException {MyThread myThread = new MyThread();FutureTask<Integer> futureTask = new FutureTask<>(myThread);Thread t1 = new Thread(futureTask);t1.start();Integer sum = futureTask.get();System.out.println("sum = " + sum);}}
注意:get方法時(shí),方法會(huì)阻塞,直到這個(gè)線程執(zhí)行完畢有返回值,代碼才會(huì)往下進(jìn)行.
4. 守護(hù)線程
守護(hù)線程
- 守護(hù)線程是一種特殊的線程,
它并不屬于用戶線程
,也不受用戶線程的控制,但它卻有著特殊的重要性。 - 守護(hù)線程的主要目的是為其他非守護(hù)線程提供服務(wù)。
當(dāng)所有的用戶線程都結(jié)束時(shí),守護(hù)線程也會(huì)隨之結(jié)束
。 - 守護(hù)線程的創(chuàng)建方式是在啟動(dòng)線程時(shí)調(diào)用
Thread.setDaemon(true)
方法,該方法將線程設(shè)置為守護(hù)線程。 - 判斷當(dāng)前線程是否為守護(hù)線程調(diào)用
Thread.currentThread().isDaemon()
方法,true為是,false為不是
守護(hù)線程的退出條件是:
- 所有用戶線程都結(jié)束;
- 調(diào)用了Thread.stop()方法;
- 調(diào)用了Runtime.exit()方法。
注意:
- 守護(hù)線程只能有一個(gè),一個(gè)JVM中只能有一個(gè)守護(hù)線程;
- 守護(hù)線程不能執(zhí)行用戶線程的run()方法,因?yàn)樗鼪](méi)有用戶線程可執(zhí)行;
- 守護(hù)線程的優(yōu)先級(jí)比較低,它只會(huì)在JVM中運(yùn)行,不會(huì)影響到其他線程的運(yùn)行;
- 守護(hù)線程的異常處理方式和非守護(hù)線程相同。
public class ProtectThread {public static void main(String[] args) {//創(chuàng)建一個(gè)線程Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + ": " +(Thread.currentThread().isDaemon() ? " 守護(hù)線程" : "用戶線程"));while (true) {}},"t1");t1.setDaemon(true);//設(shè)置為守護(hù)線程t1.start();System.out.println(Thread.currentThread().getName() + ": " +(Thread.currentThread().isDaemon() ? " 守護(hù)線程" : "主線程"));}}
5. windows,linux下查看和殺死進(jìn)程
5.1windows
-
任務(wù)管理器可以查看進(jìn)程和線程數(shù),也可以用來(lái)殺死進(jìn)程.
-
tasklist
查看進(jìn)程
-
taskkill
殺死進(jìn)程- 通常帶兩個(gè)參數(shù) /F 強(qiáng)制殺死 /PID 進(jìn)程id
5.2 linux
-
ps -ef 查看所有進(jìn)程
-
ps -fT -p 查看某個(gè)進(jìn)程(PID)的所有線程
- kill 殺死進(jìn)程
- top按大寫H切換是否顯示線程
動(dòng)態(tài)展示所有進(jìn)程占用cpu的情況
- top -H -p 查看某個(gè)進(jìn)程(PID)的所有線程
5.3 查看Java的進(jìn)程
-
jps
命令查看所有 Java進(jìn)程 -
jstack
查看某個(gè)Java進(jìn)程 (PID) 的所有線程狀態(tài).
-
jconsole
來(lái)查看某個(gè)Java進(jìn)程中線程的運(yùn)行情況(圖形界面)
6. 線程運(yùn)行原理
我們知道JVM
虛擬機(jī)中有棧和堆兩塊內(nèi)存區(qū)域.
而每一個(gè)線程都有屬于自己的棧區(qū),每個(gè)線程直接的?;ゲ桓蓴_,線程中每一個(gè)方法都是一個(gè)棧幀.
看如下這段代碼的調(diào)度過(guò)程:
我們?cè)偻ㄟ^(guò)一幅圖來(lái)理解
7. 線程的上下文切換
有些時(shí)候由于一些原因,當(dāng)線程在執(zhí)行時(shí),cpu被其他線程搶占到也就是說(shuō)存在線程之間的切換的過(guò)程,我們稱為上下文切換.
哪些原因會(huì)導(dǎo)致線程上下文切換呢
- 線程的cpu時(shí)間片用完
- 垃圾回收
- 有更高優(yōu)先級(jí)的線程需要運(yùn)行
- 線程自己調(diào)用了sleep,yield,wait,join,park,synchronized,lock等方法
當(dāng)上下文切換時(shí),需要由操作系統(tǒng)去保存當(dāng)前線程的狀態(tài),也就是記錄其中的一些變量啊,執(zhí)行到哪一步啦,并恢復(fù)另一個(gè)線程的狀態(tài),Java中對(duì)應(yīng)的概念就是程序計(jì)數(shù)器
,它的作用是記住下一條JVM指令的執(zhí)行地址,是線程自己私有的
- 狀態(tài)包括程序計(jì)數(shù)器,虛擬機(jī)棧中每個(gè)棧幀的信息,如
局部變量
,操作數(shù)棧
,返回地址
等
注意:頻繁的上下文切換會(huì)影響性能.
8. 線程的幾種狀態(tài)
8.1 五種(來(lái)源于操作系統(tǒng))
初始狀態(tài)
: 相當(dāng)于我們剛剛new出來(lái)一個(gè)線程對(duì)象.僅是在語(yǔ)言層面創(chuàng)建了線程對(duì)象,還并未與操作系統(tǒng)線程相關(guān)聯(lián).可運(yùn)行狀態(tài)(就緒狀態(tài))
: 指該線程已經(jīng)被創(chuàng)建(與操作系統(tǒng)線程關(guān)聯(lián)),可以由CPU調(diào)度執(zhí)行.也就是都準(zhǔn)備去搶占cpu啦,如果搶到了就會(huì)進(jìn)入運(yùn)行狀態(tài)運(yùn)行狀態(tài)
: 指獲取了CPU時(shí)間片運(yùn)行中的狀態(tài).- 當(dāng)CPU時(shí)間片用完會(huì)從運(yùn)行狀態(tài)轉(zhuǎn)換至就緒狀態(tài), 會(huì)導(dǎo)致線程的上下文切換
阻塞狀態(tài)
- 如果調(diào)用了阻塞的API,例如BIO(同步阻塞IO)讀寫文件,這時(shí)該線程不會(huì)用到CPU,會(huì)導(dǎo)致線程上下文切換,進(jìn)入阻塞狀態(tài).
- 等BIO操作完畢,會(huì)由操作系統(tǒng)喚醒阻塞的線程,轉(zhuǎn)換至可運(yùn)行狀態(tài).
- 與可運(yùn)行狀態(tài)的區(qū)別是,對(duì)阻塞狀態(tài)的線程來(lái)說(shuō)只要它們一直不喚醒,調(diào)度器一直不會(huì)考慮調(diào)度它們.
可以這樣理解: 好比去上廁所,你搶到廁所的使用權(quán)啦,但是突然你又不想上啦,想起還有其他事情要做,總不能占著茅坑不拉屎叭,所以cpu,也就是廁所,你得離開這里,這個(gè)時(shí)候你去干其他的事都可以,等你忙完又想上廁所啦,這個(gè)時(shí)候又和別人一樣需要有機(jī)會(huì)才能搶到廁所的使用權(quán),也就是重新回到就緒狀態(tài).
終止?fàn)顟B(tài)
: 表示線程已經(jīng)執(zhí)行完畢,生命周期已經(jīng)結(jié)束,不會(huì)在轉(zhuǎn)換為其他狀態(tài).也就是代碼執(zhí)行完畢,沒(méi)有循環(huán)的包裹,執(zhí)行完就會(huì)進(jìn)入終止?fàn)顟B(tài).
8.2 六種(Java層面)
我們來(lái)查看Thread類當(dāng)中的一個(gè)枚舉類State
所以一共有六種狀態(tài),其具體為
- NEW 線程剛被創(chuàng)建,但是還沒(méi)有調(diào)用start()方法
- RUNNABLE當(dāng)調(diào)用了start()方法之后.
注意: JavaAPI層面的RUNNABLE狀態(tài)涵蓋了操作系統(tǒng)層面的可運(yùn)行狀態(tài)(就緒狀態(tài)),運(yùn)行狀態(tài),阻塞狀態(tài)(由于BIO導(dǎo)致的線程阻塞,在Java里無(wú)法區(qū)別,仍然認(rèn)為是可運(yùn)行,也就是RUNNABLEZ狀態(tài))
- BLOCKED 可以理解為搶占鎖時(shí)未搶到,造成阻塞等待
- WAITING 等待別的線程執(zhí)行結(jié)束
- TIMED_WATING 有時(shí)間的等待,也就是對(duì)應(yīng)Thread.sleep();
- TERMINATED 當(dāng)線程代碼運(yùn)行結(jié)束
8.2.1 代碼實(shí)現(xiàn)每種狀態(tài)
-
NEW (創(chuàng)建線程不開始)
-
RUNNABLE(就緒,運(yùn)行,阻塞(操作系統(tǒng)的層面例如IO流時(shí)))
-
TERMINATED (代碼執(zhí)行完畢就進(jìn)入此狀態(tài))
-
TIMED_WATING (睡眠時(shí)進(jìn)入)
-
WAITING(利用join方法,使線程2等待線程1執(zhí)行完畢,此時(shí)狀態(tài)為WAITING)
-
BLOCKED(阻塞,搶占鎖失敗,阻塞等待)