執行緒的基本方法

  • 2020 年 3 月 10 日
  • 筆記

1. 進程與執行緒

進程:資源分配的基本單位

執行緒:資源調度的基本單位

1.1 有了進程為什麼還需要執行緒呢?

為了進程能進行並發操作

1.2 執行緒的生命周期

2. 創建進程

創建進程有兩種方法(一般推薦第二種,因為單繼承問題)

先來看看執行緒的構造函數

2.1 繼承Thread類,重寫run()

public class Threadtest extends Thread {        //設置名字      public Threadtest(String name) {          super(name);      }        //重寫方法      public void run(){          for(int i = 0;i < 100;i++){              System.out.println(Thread.currentThread().getName() + "-----" + i);          }      }        public static void main(String[] args) {            Threadtest t1 = new Threadtest("執行緒1");          Threadtest t2 = new Threadtest("執行緒2");            t1.start();          t2.start();      }  }

2.2 實現Runnable介面,重寫run()

public class Runnabletest 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[] args) {            Thread t1 = new Thread(new Runnabletest(),"執行緒1");          Thread t2 = new Thread(new Runnabletest(),"執行緒2");            t1.start();          t2.start();      }  }
// 上面兩種方法都是執行緒交替進行  執行緒1:::0  執行緒1:::1  執行緒1:::2  執行緒1:::3  執行緒1:::4  執行緒2:::0  執行緒1:::5  執行緒1:::6  執行緒1:::7  執行緒1:::8  執行緒1:::9  執行緒1:::10  執行緒2:::1

3.執行緒的方法

3.1 命名 getName()

  • 該構造函數里的init方法的第三個參數是執行緒名
  • 第三個參數是個函數,該函數同步地維護了threadInitNumber,是一個數字
  • 可想而知,執行緒名字是 Thread-Number,eg: Thread-0
  • 從2.1/2.2可知構造方法裡面提供了命名執行緒的方式
/**   * Allocates a new {@code Thread} object. This constructor has the same   * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}   * {@code (null, null, gname)}, where {@code gname} is a newly generated   * name. Automatically generated names are of the form   * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.   */  public Thread() {      init(null, null, "Thread-" + nextThreadNum(), 0);  }    /* For autonumbering anonymous threads. */  private static int threadInitNumber;  private static synchronized int nextThreadNum() {      return threadInitNumber++;  }
  • 也可以通過 setName(String name) 來命名,不過該方法調用native方法,筆者水平有限不做深究
Thread thread = new Thread(() -> {      System.out.println(Thread.currentThread().getName());  });  thread.setName("新執行緒名");
新執行緒名

3.1 守護執行緒(setDaemon)

  • 守護進程是為其他執行緒服務的執行緒,存在於後台,一旦有執行緒就存在,執行緒全部消失而結束,eg: 垃圾回收執行緒
  • 守護執行緒中產生的新執行緒也是守護執行緒
  • 需要在進程啟動前調用Thread.setDaemon(true),會用native方法檢測,非法則拋出異常
public final void setDaemon(boolean on) {      checkAccess();      if (isAlive()) {          throw new IllegalThreadStateException();      }      daemon = on;    //設置守護執行緒為真  }    /* Whether or not the thread is a daemon thread. */  // private boolean  daemon = false;

3.2 優先順序(setPriority)

  • 設置獲取CPU時間片的幾率,分0—10等級,默認為5
// MAX_PRIORITY 最大級別10  // MIN_PRIORITY 最小0  // NORM_PRIORITY 普通5    public final void setPriority(int newPriority) {      ThreadGroup g;  //執行緒組      checkAccess();      if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {          throw new IllegalArgumentException();      }      if((g = getThreadGroup()) != null) {          if (newPriority > g.getMaxPriority()) {              newPriority = g.getMaxPriority();          }          setPriority0(priority = newPriority);  //設置級別,後面遇到native方法不說明了      }  }
public static void main(String[] args) {            Threadtest t1 = new Threadtest("執行緒1");          Threadtest t2 = new Threadtest("執行緒2");            //設置優先順序          t1.setPriority(10);            t1.start();          t2.start();  }
執行緒1:::97  執行緒1:::98  執行緒1:::99  執行緒2:::3  執行緒2:::4  執行緒2:::5

在前面實例中調用該函數,發現t1執行緒cpu執行時間片多於t2執行緒,t1完成了t2還在開頭

3.3 sleep

  • 讓該執行緒休眠,不釋放鎖
  • 結束重回就緒狀態
  • t1.sleep(),不是t1睡眠,而是當前執行緒睡眠
  • 哪個執行緒調用sleep方法,哪個執行緒就睡眠
public class Threadtest extends Thread {        //設置名字      public Threadtest(String name) {          super(name);      }        //重寫方法      public void run(){          for(int i=0; i < 100;i++){              System.out.println(Thread.currentThread().getName() + "--" + i);          }        }        public static void main(String[] args) throws InterruptedException {          Threadtest t2 = new Threadtest("執行緒2");            t2.start();          t2.sleep(10000);            System.out.println("=========");      }  }
執行緒2--96  執行緒2--97  執行緒2--98  執行緒2--99  //10秒後,main執行緒睡眠  =======

3.4 join

  • 使當前執行緒停下來等待,直到調用join()的執行緒結束,才恢復執行
  • 它可以使得執行緒之間的並行執行變為串列執行
  • 在start之後才執行的
  • 底層還是調用wait方法
public class Threadtest extends Thread {        //設置名字      public Threadtest(String name) {          super(name);      }        //重寫方法      public void run(){          for(int i=0; i < 100;i++){              System.out.println(Thread.currentThread().getName() + "--" + i);          }        }        public static void main(String[] args) throws InterruptedException {            Threadtest t1 = new Threadtest("執行緒1");          Threadtest t2 = new Threadtest("執行緒2");            t2.setPriority(10);          t1.start();          t1.join();      //main執行緒停下來,等t1執行完才繼續往下執行,所以先輸入t1,再輸出t2          t2.start();      }  }

3.5 wait和notify

  • wait使當前執行緒掛起,notify隨機喚醒一個同享對象鎖的執行緒,notifyAll喚醒所有
  • wait必須在同步程式碼塊或同步方法中調用,先要有鎖才能釋放鎖
  • wait方法釋放鎖,並處於阻塞狀態
  • notify不是立即釋放鎖,要執行完同步操作才釋放鎖
  • 被其他執行緒喚醒後處於就緒狀態
thread.wait();  thread.notify();

3.6 yield

使當前執行緒從運行狀態轉為就緒狀態,即可能讓別的執行緒執行,也可能自己再次執行

3.7 interrupt

  • 該方法不是即時中斷執行緒,而是僅僅設置一個中斷訊號量,然後中斷操作由我們自己實現
Thread t1 = new Thread(new Runnable(()->{      // 若未發生中斷,就正常執行任務      while(!Thread.currentThread.isInterrupted()){          // 正常任務程式碼……      }      // 中斷的處理程式碼……      doSomething();  }).start();
  • 當執行緒在活動之前或活動期間處於阻塞狀態(正在等待、休眠或佔用狀態)且該執行緒被中斷時,拋出InterruptedException
  • 阻塞執行緒調用interrupt()方法,會拋出異常,設置標誌位為false,同時該執行緒會退出阻塞

3.8 exit

退出當前執行緒(或者當run方法結束也是執行緒結束)

3.9 start和run區別

  • run():僅僅是封裝被執行緒執行的程式碼,直接調用是普通方法
  • start():創建執行緒,jvm調用執行緒的run()方法,所以start方法執行完,不代表run方法執行完,執行緒也不一定銷毀!

3.10 currentThread()

獲取當先運行的執行緒,Thread thread = Thread.currentThread(),屬於靜態方法