Java知識回顧 (10) 線程

再次聲明,正如(1)中所描述的,本資料來自於runoob,略有修改。html

 

一條線程指的是進程中一個單一順序的控制流,一個進程中能夠併發多個線程,每條線程並行執行不一樣的任務。java

Java 給多線程編程提供了內置的支持。 程序員

多線程是多任務的一種特別的形式,但多線程使用了更小的資源開銷。編程

這裏定義和線程相關的另外一個術語 - 進程:一個進程包括由操做系統分配的內存空間,包含一個或多個線程。一個線程不能獨立的存在,它必須是進程的一部分。一個進程一直運行,直到全部的非守護線程都結束運行後才能結束。多線程

多線程能知足程序員編寫高效率的程序來達到充分利用 CPU 的目的。併發

1、生命週期

線程是一個動態執行的過程,它也有一個從產生到死亡的過程。dom

下圖顯示了一個線程完整的生命週期。ide

  • 新建狀態:

    使用 new 關鍵字和 Thread 類或其子類創建一個線程對象後,該線程對象就處於新建狀態。它保持這個狀態直到程序 start() 這個線程。this

  • 就緒狀態:

    當線程對象調用了start()方法以後,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM裏線程調度器的調度spa

  • 運行狀態:

    若是就緒狀態的線程獲取 CPU 資源,就能夠執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最爲複雜,它能夠變爲阻塞狀態、就緒狀態和死亡狀態。

  • 阻塞狀態:

    若是一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源以後,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或得到設備資源後能夠從新進入就緒狀態。能夠分爲三種:

    死亡狀態:
    • 等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。

    • 同步阻塞:線程在獲取 synchronized 同步鎖失敗(由於同步鎖被其餘線程佔用)。

    • 其餘阻塞:經過調用線程的 sleep() 或 join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程從新轉入就緒狀態。

  • 一個運行狀態的線程完成任務或者其餘終止條件發生時,該線程就切換到終止狀態。

2、線程的優先級

每個 Java 線程都有一個優先級,這樣有助於操做系統肯定線程的調度順序。

Java 線程的優先級是一個整數,其取值範圍是 1 (Thread.MIN_PRIORITY ) ~ 10 (Thread.MAX_PRIORITY )。

默認狀況下,每個線程都會分配一個優先級 NORM_PRIORITY(5)。

具備較高優先級的線程對程序更重要,而且應該在低優先級的線程以前分配處理器資源。可是,線程優先級不能保證線程執行的順序,並且很是依賴於平臺。

3、建立

Java 提供了三種建立線程的方法:

  • 經過實現 Runnable 接口;
  • 經過繼承 Thread 類自己;
  • 經過 Callable 和 Future 建立線程。

3.1 Demo

1)Runnable

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;
   
   RunnableDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // 讓線程睡眠一會
            Thread.sleep(50);
         }
      }catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}
 
public class TestThread {
 
   public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo( "Thread-1");
      R1.start();
      
      RunnableDemo R2 = new RunnableDemo( "Thread-2");
      R2.start();
   }   
}

2)Thread方式

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // 讓線程睡眠一會
            Thread.sleep(50);
         }
      }catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}
 
public class TestThread {
 
   public static void main(String args[]) {
      ThreadDemo T1 = new ThreadDemo( "Thread-1");
      T1.start();
      
      ThreadDemo T2 = new ThreadDemo( "Thread-2");
      T2.start();
   }   
}

Thread類的一些方法

// 文件名 : DisplayMessage.java
// 經過實現 Runnable 接口建立線程
public class DisplayMessage implements Runnable {
   private String message;
   
   public DisplayMessage(String message) {
      this.message = message;
   }
   
   public void run() {
      while(true) {
         System.out.println(message);
      }
   }
}

 

// 文件名 : GuessANumber.java
// 經過繼承 Thread 類建立線程
 
public class GuessANumber extends Thread {
   private int number;
   public GuessANumber(int number) {
      this.number = number;
   }
   
   public void run() {
      int counter = 0;
      int guess = 0;
      do {
         guess = (int) (Math.random() * 100 + 1);
         System.out.println(this.getName() + " guesses " + guess);
         counter++;
      } while(guess != number);
      System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
   }
}

 

// 文件名 : ThreadClassDemo.java
public class ThreadClassDemo {
 
   public static void main(String [] args) {
      Runnable hello = new DisplayMessage("Hello");
      Thread thread1 = new Thread(hello);
      thread1.setDaemon(true);
      thread1.setName("hello");
      System.out.println("Starting hello thread...");
      thread1.start();
      
      Runnable bye = new DisplayMessage("Goodbye");
      Thread thread2 = new Thread(bye);
      thread2.setPriority(Thread.MIN_PRIORITY);
      thread2.setDaemon(true);
      System.out.println("Starting goodbye thread...");
      thread2.start();
 
      System.out.println("Starting thread3...");
      Thread thread3 = new GuessANumber(27);
      thread3.start();
      try {
         thread3.join();
      }catch(InterruptedException e) {
         System.out.println("Thread interrupted.");
      }
      System.out.println("Starting thread4...");
      Thread thread4 = new GuessANumber(75);
      
      thread4.start();
      System.out.println("main() is ending...");
   }
}

 

3)經過 Callable 和 Future 建立線程

  • 1. 建立 Callable 接口的實現類,並實現 call() 方法,該 call() 方法將做爲線程執行體,而且有返回值。

  • 2. 建立 Callable 實現類的實例,使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call() 方法的返回值。

  • 3. 使用 FutureTask 對象做爲 Thread 對象的 target 建立並啓動新線程。

  • 4. 調用 FutureTask 對象的 get() 方法來得到子線程執行結束後的返回值。

public class CallableThreadTest implements Callable<Integer> {
    public static void main(String[] args)  
    {  
        CallableThreadTest ctt = new CallableThreadTest();  
        FutureTask<Integer> ft = new FutureTask<>(ctt);  
        for(int i = 0;i < 100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" 的循環變量i的值"+i);  
            if(i==20)  
            {  
                new Thread(ft,"有返回值的線程").start();  
            }  
        }  
        try  
        {  
            System.out.println("子線程的返回值:"+ft.get());  
        } catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        } catch (ExecutionException e)  
        {  
            e.printStackTrace();  
        }  
  
    }
    @Override  
    public Integer call() throws Exception  
    {  
        int i = 0;  
        for(;i<100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" "+i);  
        }  
        return i;  
    }  
}

 

 

3.2 建立線程的三種方式的對比

  • 1. 採用實現 Runnable、Callable 接口的方式建立多線程時,線程類只是實現了 Runnable 接口或 Callable 接口,還能夠繼承其餘類。

  • 2. 使用繼承 Thread 類的方式建立多線程時,編寫簡單,若是須要訪問當前線程,則無需使用 Thread.currentThread() 方法,直接使用 this 便可得到當前線程。

3.3 線程的幾個主要概念

在多線程編程時,你須要瞭解如下幾個概念:

  • 線程同步
  • 線程間通訊
  • 線程死鎖
  • 線程控制:掛起、中止和恢復

 

多線程的使用

有效利用多線程的關鍵是理解程序是併發執行而不是串行執行的。例如:程序中有兩個子系統須要併發執行,這時候就須要利用多線程編程。

經過對多線程的使用,能夠編寫出很是高效的程序。不過請注意,若是你建立太多的線程,程序執行的效率其實是下降了,而不是提高了。

請記住,上下文的切換開銷也很重要,若是你建立了太多的線程,CPU 花費在上下文的切換的時間將多於執行程序的時間!

相關文章
相關標籤/搜索