一、多執行緒[建立,interrupt,setDaemon,getPriority,isAlive等]
一、多執行緒簡介
在CPU上的執行緒,執行一個任務,巨集觀當然是同時執行的,但微觀裡的確實序列執行的,CPU通過執行緒中斷,讓某一個執行緒掛起來,然後切換到另一個執行緒,執行一會兒,再切換回來。但是執行緒切換來回會犧牲一定的效能,如果增加CPU那麼執行緒是可以達到並行的。
- 聯想下我們去買電影票,先不說可以網上購買,那麼們去買票人一多就要排隊去, 還是得等待(這種性質就是同步)
扯下皮:如果大家都不排隊了,在你正在買票的時候,其他人進行插隊把你的票搶走,覺得這個人神馬素質嘛,有可能讓他體驗到被揍的感覺,然後售票廳一片狼藉混亂,…,無規矩不成方圓。這個規矩就是“互斥鎖”,排隊並且不允許插隊一個個來。 - 訪問共享資源的時候要鎖住執行緒,不能讓其他人來搶劫。別人就只能等待了
- 單執行緒:好比電影院只有一個視窗,假設電影快開始了,你要買3D眼鏡,那麼你就只能等待前面的人員買完票。
- 多執行緒:多執行緒的話可以多開一個賣3D眼鏡的視窗,如果有人排隊可以處理完當前這個人的再去處理另外一個視窗,這樣買3D眼鏡的人也不用等那麼久了。
- 注意 :執行緒數如果大於物理CPU個數的時候,是可以充分利用CPU,提高效能的。但無限制的加大執行緒數會帶來執行緒切換的開銷,所以一個服務程式的最優執行緒數需要根據具體情況來具體評估
二、建立多執行緒的兩種方式
1、繼承Thread
public class JobRun extends Thread{ @Override public void run() { for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+i); } } } public static void main(String[] agrs){ JobRun jobRun=new JobRun(); jobRun.start(); //jobRun.start();再加一個就報錯了,面試中有提到過 for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }
結果:
main:0
Thread-00
main:1
Thread-02
…
2、實現Runnable
public class JobRunnable implements Runnable { @Override public void run() { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+i); } } } public static void main(String[] agrs){ JobRunnable jobRunnable=new JobRunnable(); Thread t=new Thread(jobRunnable); t.start(); for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } }
結果:
main:0
Thread-00
…
總結:大同小異,輸出方式一樣,但是java是單繼承,所以儘量選擇實現的方式。具體怎麼運作,如果不清晰把上面的的概念再理解一下。
三、執行緒的屬性與方法
1、執行順序:
那麼各個之間的執行順序呢
程式碼示例:
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] agrs){
JobRun jobRun1=new JobRun();
JobRun jobRun2=new JobRun();
JobRun jobRun3=new JobRun();
jobRun1.start();
jobRun2.start();
jobRun3.start();
System.out.println(Thread.currentThread().getName()+"-Main-:");
}
結果: 有沒有看到為什麼不是0 2 1呢。
main-Main-:
Thread-0
Thread-2
Thread-1
總結:呼叫start()方法的順序不代表執行緒啟動的順序,執行緒啟動順序具有不確定性
2、run
程式碼示例:
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] agrs){
JobRun jobRun1=new JobRun();
JobRun jobRun2=new JobRun();
JobRun jobRun3=new JobRun();
jobRun1.run();
jobRun2.run();
jobRun3.run();
System.out.println(Thread.currentThread().getName()+"-Main-:");
}
結果:
main
main
main
main-Main-:
總結:Thread例項run()方法裡面的內容是沒有任何非同步效果的,全部被main函式執行。換句話說,只有run()而不呼叫start()啟動執行緒是沒有任何意義的。
- 有沒有感覺start有點像“執行緒規劃器”
3、isAlive,getId,getName
程式碼示例:
public class JobRun extends Thread{
@Override
public void run() {
Long t1=System.currentTimeMillis();
for (int i=0;i<100000000;i++){}
Long t2=System.currentTimeMillis();
System.out.println(this.getName()+"jobRun結束時間:"+t2+" \t執行的耗時:"+(t2-t1)+"\t 的優先順序:"+this.getPriority());
}
}
public static void main(String[] agrs){
JobRun jobRun1=new JobRun();
System.out.println("執行緒是否在執行 new :"+jobRun1.isAlive());
System.out.println("new get Id :"+jobRun1.getId());
System.out.println("new get Name :"+jobRun1.getName());
jobRun1.start();
System.out.println("執行緒是否在執行 start :"+jobRun1.isAlive());
Thread.sleep(100);
System.out.println("執行緒是否在執行 new :"+jobRun1.isAlive());
System.out.println("new get Id :"+jobRun1.getId());
System.out.println("new get Name :"+jobRun1.getName());
}
結果:
執行緒是否在執行 new :false
new get Id :12
new get Name :Thread-0
執行緒是否在執行 start :true
Thread-0
執行緒是否在執行 new :false
new get Id :12
new get Name :Thread-0
總結:
- isAlive:只有在執行的時候是True,其餘的都為false。
- ID: 有一個long型的全域性唯一的執行緒ID生成器threadSeqNumber,每new出來一個執行緒都會把這個自增一次,並賦予執行緒的tid屬性,這個是Thread自己做的,使用者無法執行一個執行緒的Id。
- Name: 在new時,可以之定義執行緒的名字,getName()返回的也是自定義執行緒的名字;預設,Thread使用int型全域性唯一的執行緒初始號生成器threadInitNum,自增後,以"Thread-threadInitNum"的方式來命名新生成的執行緒。
4、getPriority()和setPriority(int newPriority)
設定優先順序有助於幫"執行緒規劃器"確定下一次選擇哪一個執行緒優先執行
兩個在等待CPU的執行緒,優先順序高的執行緒可能優先被CU選擇執行
程式碼示例:
public class JobRun_1 extends Thread{
@Override
public void run() {
Long t1=System.currentTimeMillis();
for (int i=0;i<100000000;i++){}
Long t2=System.currentTimeMillis();
System.out.println(this.getName()+"jobRun_1結束時間:"+t2+" \tjobRun執行的耗時:"+(t2-t1)+"\t 的優先順序:"+this.getPriority());
}
}
public class JobRunnable implements Runnable {
public void run() {
Long t1 = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
}
Long t2 = System.currentTimeMillis();
System.out.println("JobRunnable執行的耗時:" + (t1 - t2));
}
}
public static void main(String[] agrs) {
System.out.println("main的優先順序:"+Thread.currentThread().getPriority());
JobRun jobRun=new JobRun();
jobRun.start();
System.out.println("jobRun 的優先順序是:"+jobRun.getPriority());
for (int i =0;i<30;i++){
JobRun jobRun=new JobRun();
jobRun.setPriority(9);
jobRun.start();
JobRun_1 jobRun_1=new JobRun_1();
jobRun_1.setPriority(1);
jobRun_1.start();
}
}
結果:
main的優先順序:5
jobRunExtMain 的優先順序是:5
Thread-59jobRun_1結束時間:1541743992074 jobRun執行的耗時:4 的優先順序:1
Thread-57jobRun_1結束時間:1541743992092 jobRun執行的耗時:18 的優先順序:1
…
Thread-42jobRun結束時間:1541743992391 執行的耗時:0 的優先順序:9
Thread-38jobRun結束時間:1541743992432 執行的耗時:0 的優先順序:9
…
Thread-1jobRun_1結束時間:1541743992969 jobRun執行的耗時:0 的優先順序:1
__ 注意: __
預設執行級別是5,如果沒有指定預設繼承呼叫者的優先順序,1-10,超過了這個值就報異常。
在測試過程中,將開發工具設定成單個CPU,現在的電腦應該都是超執行緒的並且多核,系統會識別成8個CPU,指定一個就好了。顯示的效果會好一丟丟。
總結:執行緒的執行順序跟“優先順序”無關,優先順序高CPU儘可能將資源分配給它。
5、isDaeMon、setDaemon(boolean on)
Java中有兩種執行緒,使用者執行緒,守護執行緒。
守護執行緒會隨著使用者執行緒而自動摧毀
程式碼示例:
public class JobRun_2 extends Thread {
int i=0;
@Override
public void run() {
while(true){
try {
i++;
Thread.sleep(1000);
System.out.println("默默地輸出.."+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] agrs) throws InterruptedException {
JobRun_2 jobRun_2=new JobRun_2();
jobRun_2.setDaemon(true);
jobRun_2.start();
System.out.println("優先順序:"+jobRun_2.getPriority());
Thread.sleep(3000);
System.out.println("我執行完了,守護執行緒不再執行...");
}
結果:
優先順序:5
默默地輸出…1
默默地輸出…2
我執行完了,守護執行緒不再執行…
注意: setDaemon(true)必須線上程start()之前
總結:優先順序不變,隨著使用者執行緒死完,那麼它可以做什麼麼?
··守護執行緒是一種特殊的執行緒,它的作用是為其他執行緒的執行提供便利的服務,最典型的應用便是GC執行緒
6、interrupt()/isInterrupted
中斷執行緒
程式碼示例:
public int i=0;
@Override
public void run() {
while(i<10000000){
try {
System.out.println("有本事中斷我啊,繼續出招:"+Thread.currentThread().isInterrupted()+ ++i);
Thread.sleep(500);
System.out.println("出招完畢:"+Thread.currentThread().isInterrupted()+ i);
//System.out.println("出招完畢:"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] agrs) throws InterruptedException {
JobRun_3 jobRun_3=new JobRun_3();
jobRun_3.start();
Thread.sleep(2000);
jobRun_3.interrupt();
System.out.println("知道我的厲害了吧,還不點贊!");
}
__ 結果: __
有本事中斷我啊,繼續出招:false1
出招完畢:false1
有本事中斷我啊,繼續出招:false2
出招完畢:false2
有本事中斷我啊,繼續出招:false3
出招完畢:false3
有本事中斷我啊,繼續出招:false4
知道我的厲害了吧,還不點贊!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at Doem_Thread.JobRun_3.run(JobRun_3.java:15)
有本事中斷我啊,繼續出招:false5
出招完畢:false5
…
總結:呼叫中斷程式時,如果程式堵塞或者呼叫了Sleep是會丟擲異常,但是不一定會停止。程式根本不鳥中斷命令 哈哈
- 那我們加強一下
程式碼示例:
@Override
public void run() {
while (i < 10000000) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("命中要害...中斷");
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
this.interrupt();
e.printStackTrace();
}
System.out.println("有本事中斷我啊,繼續出招:" + Thread.currentThread().isInterrupted() + ++i);
System.out.println("出招完畢:" + Thread.currentThread().isInterrupted() + i);
}
}
main不改程式碼,執行..
結果:
出招完畢:false3
知道我的厲害了吧,還不點贊!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at Doem_Thread.JobRun_3.run(JobRun_3.java:18)
有本事中斷我啊,繼續出招:true4
出招完畢:true4
命中要害…中斷
原來要和isInterrupted 配合使用,不知道有沒有留意到,如果加了sleep,isInterrupted一直都為false。
中斷命令指示做了一個通知,sleep或堵塞的時候會丟擲異常,那可以利用異常去通知到下一次是否該執行。
- 擴充套件
1、多執行緒與超執行緒的區別?
相信大家以及理解了上面所講的多執行緒了
超執行緒:CPU可以一次性執行多個不同執行緒技術,不需要來回切,
超執行緒技術會讓系統以為有兩塊物理CPU,作業系統就會同時向那‘兩’塊CPU傳送2個執行緒的任務,會讓CPU嘗試同時執行兩個執行緒。主要看CPU支不支援超執行緒。
總結:兩者的區別是:多執行緒是併發,超執行緒是並行