1. 程式人生 > 實用技巧 >十四、執行緒

十四、執行緒

1.什麼是程序?什麼是執行緒?

  程序是一個應用程式(一個程序一個軟體)

  執行緒是一個程序中的執行場景/執行單元

  一個程序可以啟動多個執行緒

2.對於java來說,啟動JVM,JVM在啟動一個主執行緒呼叫main方法,同時再啟動一個垃圾回收器執行緒負責看護,回收垃圾。

3.程序與執行緒的關係?

程序與程序之間記憶體獨立不共享。

執行緒與執行緒之間在java語言中:

  堆記憶體和方法去記憶體共享。

  棧記憶體獨立,一個執行緒一個棧。

4.多執行緒機制可以提高程式處理效率。

5.使用對執行緒機制之後,main方法結束,程式可能不會結束。main方法結束,只是主棧空了,其他棧(執行緒)可能還在壓棧彈棧。

6.JVM虛擬機器:

  方法區、堆記憶體只有一個。多執行緒共享 。

  一個棧一個執行緒。支棧:分支執行緒。

7.什麼是多執行緒併發?

  t1執行緒執行t1的,t2執行緒執行t2的。

  t1不會影響t2,t2不會影響t1。這才叫多執行緒併發。

8.對於一個單核的CPU來說,真的可以做到多執行緒併發嗎?

  對於多核的CPU電腦,真正的多執行緒併發是沒有問題的。

單核的CPU表示只有一個大腦:

  不能夠做到真正的多執行緒併發,但是可以給人一種”多執行緒併發“的感覺。對於單核的CPU來說,在某一個時間點上實際上只能處理一件事情,但是由於CPU執行速度處理極快,多個執行緒之間頻繁切換執行,給人的感覺就是同時在做。

9.實現執行緒的方式:

java支援多執行緒機制,並且java已經將多執行緒實現了,我們只需要繼承就行。

第一種:編寫一個類,直接繼承java.lang.Thread,重寫run方法。

  怎麼建立執行緒物件? new就行了

  怎麼啟動執行緒? 呼叫執行緒物件的start()方法。

public class Test{

    public static void main(String[] args) {
        //主執行緒在主棧中
        
        //建立分支執行緒物件
        MyThread th = new MyThread();
        
        
//啟動執行緒 th.start(); //start()方法的作用:啟動一個分支執行緒,在JVM中開闢一個新的棧空間, //這段程式碼任務完成之後,瞬間就結束了。 //這段程式碼的任務只是為了開啟一個新的棧空間,只要新的棧空間開出來, //start()方法就結束了。執行緒就啟動成功了。 //啟動成功的執行緒會自動呼叫run()方法並且run()方法在分支線的棧底部(壓棧)。 //run方法在分支執行緒的底部,main方法在主棧的棧底部,run和main是平級的。 //直接呼叫run方法,與呼叫start的方法有啥區別? //th.run();//不會啟動執行緒,不會分配新的分支棧。(等於在同一個棧中,單執行緒) //這裡的程式碼還是執行在主執行緒中 for(int i = 0; i < 100;i++) { System.out.println("main"); } } }
View Code

以上程式碼輸出結果有這樣的特點:有先有後。有多有少。Why?

某個執行緒搶到執行權。

第二種實現執行緒方式:編寫一個類實現java.lang.Runnable介面

推薦介面:面向介面程式設計。一個類實現了介面,它還可以去繼承其它的類,更靈活。

class MyRunable implements Runnable{

    @Override
    public void run() {
        for(int i = 0; i < 100;i++) {
            System.out.println("MyRunabale");
        }
    }
    
}
public class Test{

    public static void main(String[] args) {
        //建立一個可執行的物件
        MyRunable r = new MyRunable();
        //將可執行的物件封裝成一個執行緒物件
        Thread t = new Thread(r);
        
        //合併程式碼
        //Thread tt = new Thread(new MyRunable());
        //啟動執行緒
        t.start();
        for(int i = 0; i < 100;i++) {
            System.out.println("main");
        }
    }

}
View Code

第三種採用匿名內部類的方法去實現:

public class Test{

    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            
            @Override
            public void run() {
                for(int i = 0; i < 100;i++) {
                    System.out.println("Runabale");
                }
            }
        });
        
        //啟動執行緒
        t.start();
        
        for(int i = 0; i < 100;i++) {
            System.out.println("mian");
        }
    }

}
View Code

10.執行緒生命週期:

  新建狀態、就緒狀態、執行狀態、執行狀態、阻塞狀態、死亡狀態

11、關於執行緒的一些操作:

11.1、怎麼獲取執行緒物件?

  public static native Thread currentThread()

Thread t = Thread.currentThread();//返回t就是當前執行緒。

11.2、執行緒物件的名字:執行緒物件 .getName()

11.3、修改執行緒物件名字:執行緒物件 .setName("")

11.4、當執行緒沒有設定名字:Thread-0、Thread-1

public class Test{

    public static void main(String[] args) {
        
        MyRun h = new MyRun();
        h.doSome();
        
        System.out.println();
        
        //這個程式碼出現在main中,所以執行緒就是主執行緒。
        Thread tt = Thread.currentThread();
        System.out.println(tt.getName());//main
        
        
        MyRun t = new MyRun();
        //t.setName("t1");
        String tName = t.getName();    //預設Thread-0
        System.out.println(tName);
        
        MyRun t2 = new MyRun();
        String tName2 = t2.getName();    //Thread-1
        System.out.println(tName2);
        
        System.out.println();
        
        t.start();
        t2.start();
    }

}
View Code

11.5、關於執行緒sleep()方法:

  public static native void sleep(long millis) throws InterruptedException

  • 靜態方法
  • .引數是毫秒
  • 作用:讓【當前執行緒】進入休眠狀態,放棄CPU時間片,讓給其他先程使用。
  • 實現:間隔特定時間,去執行一段特定的程式碼.
public class Test{

    public static void main(String[] args) {
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("HelloWorld");
        
        for(int i = 0;i<10;i++){
            System.out.println(Thread.currentThread().getName() + i);
            
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
View Code

11.6、終止執行緒的睡眠(不是終斷執行緒的執行)

  中斷睡眠的方式依靠了java的異常處理機制java.lang.InterruptedException: sleep interrupted

class MyRun1 implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+ "--> begin");
        try {
            //子類重寫父類不能丟擲比父類更多、更寬的異常
            Thread.sleep(1000*60*60);
        } catch (InterruptedException e) {
            //列印異常資訊
            e.printStackTrace();
        }
        
        System.out.println(Thread.currentThread().getName()+ "--> end");
    }
        
}

public class Test{

    public static void main(String[] args) {
        Thread t = new Thread(new MyRun1());
        t.setName("t");
        t.start();
        
        //希望在5秒後,t執行緒醒來
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        //終斷t執行緒的睡眠
        t.interrupt();
        
        
    }

}
View Code

12.關於多執行緒併發環境下,資料的安全問題

12.1、什麼時候資料在多執行緒併發的環境下會存在安全問題呢?

  • 多執行緒併發
  • 有共享資料
  • 共享資料有修改的行為

滿足以上三個條件之後,就會存線上程安全問題。

12.2、怎麼解決執行緒安全問題?

  執行緒同步機制:執行緒排隊執行。(不能併發)

  會犧牲一部分效率。(安全第一)

12.3、synchronized的三種方法:

第一種:同步程式碼塊
synchronized(執行緒共享物件){
  同步程式碼塊;
}

第二種:在例項方法上使用synchronized
  表示共享物件一定是this
  並且同步程式碼塊是整個方法體。

第三種:在靜態方法上使用synchronized
  表示找類鎖。
  類鎖永遠只有1把。

JavaSE更新基本結束,後續或許會補充,或許不會。

上一篇:十三、IO流

下一篇:資料結構:持續更新中