1. 程式人生 > >一、多執行緒[建立,interrupt,setDaemon,getPriority,isAlive等]

一、多執行緒[建立,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支不支援超執行緒。

總結:兩者的區別是:多執行緒是併發,超執行緒是並行