Java線程池一:線程基礎

最近精讀Netty源碼,讀到NioEventLoop部分的時候,發現對Java線程&線程池有些概念還有困惑, 因此深刻總結一下
Java線程池一:線程基礎
Java線程池二:線程池原理html

線程建立

Java線程建立主要有三種方式:繼承Thread類、實現Runable接口、實現Callable接口java

只有經過調用Thread.start() 方法纔會真正建立一個線程, 調用Thread.run() 並不會多線程

當調用線程關心任務執行結果時,咱們應選擇實現Callable接口的方式建立線程異步

  • 繼承方式實現建立線程ide

    @Test
    public void testCreate_1() {
      Thread t = new Thread() {
        @Override
        public void run() {
          System.out.println(Thread.currentThread().getName());
          throw new RuntimeException();
        }
      };
      t.start();
      t.run();
    }
  • 實現Runnable接口的方式建立線程,這種方式調用線程沒法感知任務線程執行結果(是否執行、成功或者異常)oop

    @Test
    public void testCreate_2() {
      Thread t = new Thread(() -> System.out.println(Thread.currentThread().getName()));
      t.start();
    }
  • 實現Callable接口,調用線程經過FutureTask對象獲取執行結果(返回值或者異常)線程

    @Test
    public void testCreate_3() throws ExecutionException, InterruptedException {
      FutureTask<Integer> task = new FutureTask<>(() -> {
        throw new RuntimeException();
      });
      new Thread(task).start();
      System.out.println(task.get());
    }

線程的狀態

咱們知道Java線程是使用系統內核線程實現, 因此先來簡單回顧下系統內核線程的狀態code

內核線程的狀態

  • Ready狀態:當前線程已經就緒,等待系統調度
  • Running狀態:當Ready狀態的線程分配到時間片後進入該狀態
  • Blocking狀態:運行中的線程由於其餘資源未就緒進入該狀態

內核線程的狀態

Java線程的狀態

  • NEW: 實例化一個Thread對象後未調用start方法前都是該狀態
  • RUNNABLE: JVM裏面的可執行狀態,對應內核線程的Ready或者Running狀態。因此該狀態下線程不必定在在運行,有可能在等待調度
  • WAITING: 等待狀態,須要其餘線程喚醒後才能從新進入RUNNABLE狀態
  • TIMED_WAITING:超時等待,等待必定的時間或者被其餘線程喚醒以後可再進入RUNNABLE狀態
  • BLOCKED:阻塞狀態,特指等待進入synchronized同步塊的狀態(獲取監視器鎖)
  • Termination:終態

只有待獲取監視器鎖時纔是阻塞狀態,獲取Java語言實現的鎖(ReentrantLock等)是等待狀態。兩者的區別在於監視器鎖的實現依賴內核變量。htm

Java線程狀態

異常處理

假設一段業務邏輯沒有考慮運行時異常, 而運行時異常又恰好發生了,那麼對應的線程就會直接崩潰。因此多線程環境下爲了讓程序更加健壯穩定, 咱們須要捕獲異常。對象

  • 將整個業務邏輯加上異常捕獲(固然代碼就不是很優雅)

    @Test
    public void testExceptionHandle_1() {
      new Thread(() -> {
        try {
          //business code
          int a = 1, b = 0;
          a = a / b;
        } catch (Throwable th) {
          //log
        }
      }).start();
    }
  • 使用FutureTask異步回掉處理異常(更加優雅,業務邏輯和異常處理邏輯分離)

    @Test
    public void testExceptionHandle_2() {
      //業務邏輯
      FutureTask<Integer> ft = new FutureTask<>(() -> {
      //business code
      int a = 1, b = 0;
      a = a / b;
      return a;
      });
    
      Thread t = new Thread(() -> {
      ft.run();
      handleResult(ft);
      });
      t.start();
    }
    //異常處理邏輯
    private void handleResult(FutureTask<Integer> ft) {
        try {
        System.out.println("the result is " + ft.get());
        } catch (InterruptedException e) {
        //log or ...
        e.printStackTrace();
        } catch (ExecutionException e) {
        //log or ...
        e.printStackTrace();
        }
    }

中斷

Java中斷是一種線程間通訊手段。好比A線程給B線程發送一箇中斷信號,B線程收到這個信號,能夠處理也能夠不處理。

  • 中斷相關的API
void thread.interrupt();//實例方法-中斷線程(線程的中斷標識位置爲1)
boolean thread.isInterrupted();//線程是否中斷 & 不清除中斷標識
static boolean Thread.interrupted();//當前線程是否中斷 & 清除中斷標識
  • 實例

    線程t_1每次循環會判斷當前線程的中斷狀態,若是當前線程已經被中斷(中斷標識位爲1)就直接返回;

    整個通訊過程:主線程把t_1線程的中斷標識位置爲1,t_1獲取到中斷標識位爲1, 而後結束循環。

    @Test
    public void testInterrupt() throws InterruptedException {
      Thread t_1 = new Thread(() -> {
        int i = 0;
        while (true) {
          boolean isInterrupt = Thread.interrupted();
          if (isInterrupt) {
            System.out.println("i am interrupt, return");
            return;
          }
          //business code
          if (i++ % 10000 == 0) {
            System.out.println(i);
          }
        }
      });
      t_1.start();
      for (int i = 0; i < 100000; i++) {
        ;
      }
      t_1.interrupt();
    }

其餘

  • 守護線程

    當JVM中的全部的用戶線程都退出後守護線程也會退出

  • 優先級

    線程的優先級越高,越有可能更快的執行或者得到更多的可執行時間單元。可是Java線程的優先級只是參考,依賴於具體的實現

  • thread.join()

    調用線程進入WAITING狀態,直到thread線程終止

  • thread.yield()

    當前線程讓出cpu資源,仍處於RUNNABLE狀態

相關文章
相關標籤/搜索