Java多線程併發01——線程的創建與終止,你會幾種方式

本文開始將開始介紹 Java 多線程與併發相關的知識,多謝各位一直以來的關注與支持。關注我的公衆號「Java面典」瞭解更多 Java 相關知識點。

線程的創建方式

在 Java 中,用戶常用的主動創建線程的方式有三種,分別是 繼承 Thread 類實現 Runnable 接口通過Callable 和 Future

繼承 Thread 類

  • 定義 Thread 類的子類,並重寫該類的 run 方法;
  • 調用線程對象的 start() 方法來啓動該線程。

通過繼承 Thread 實現的線程類,多個線程間無法共享線程類的實例變量(需要創建不同 Thread 對象)。

/**
 * 通過繼承Thread實現線程
 */
public class MyThread extends Thread {
    public void run() {
        System.out.println("MyThread.run()");
    }
}

MyThread myThread = new MyThread();
myThread.start();

實現 Runnable 接口

  • 如果自己的類已經 extends 另一個類,就無法直接 extends Thread,此時,可以實現一個 Runnable 接口;
  • 調用線程對象的start()方法來啓動該線程。
/**
 * 通過實現Runnable接口實現的線程類
 */
public class RunnableTest implements Runnable {
    @Override
    public void run() {
        System.out.println("RunnableTest.run()");
    }

    public static void main(String[] args) {
        RunnableTest runnableTest = new RunnableTest() ;
        Thread thread = new Thread(runnableTest);
        thread.start();
    }
}

通過 Callable、Future

從 Thread 和 Runnable 兩種方式可以看出,兩種方式都不支持返回值,且不能聲明拋出異常

而 Callable 接口則實現了此兩點,Callable 接口如同 Runable 接口的升級版,其提供的 call() 方法將作爲線程的執行體,同時允許有返回值。

但是 Callable 對象不能直接作爲 Thread 對象的 target,我們可以使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call() 方法的返回值。

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class CallableTest {
    public static void main(String[] args) {
        CallableTest callableTest = new CallableTest() ;
        //因爲Callable接口是函數式接口,可以使用Lambda表達式
        FutureTask<String> task = new FutureTask<Integer>((Callable<String>)()->{
          System.out.println("FutureTask and Callable");
          return "hello word";
        });

       try{
           System.out.println("子線程返回值 : " + task.get());
        } catch (Exception e){
           e.printStackTrace();
        }
    }
}

線程的終止方式

線程除了正常結束外,還可以通過特定方式終止線程,終止線程常用的方式有以下三種:使用退出標誌退出線程、** Interrupt 方法結束線程stop 方法終止線程**。

使用退出標誌退出線程

最常使用的方式其實現方式是:定義一個 boolean 型的標誌位,在線程的 run() 方法中根據這個標誌位是 true 還是 false 來判斷是否退出,這種情況一般是將任務放在 run() 方法中的一個 while 循環中執行的。

public class ThreadSafe extends Thread {
    public volatile boolean exit = false;
    public void run() {
        while (!exit){
            //do work
        }
    }

    public static void main(String[] args) throws Exception  {  
        ThreadFlag thread = new ThreadFlag();  
        thread.start();  
        sleep(5000); // 主線程延遲5秒  
        thread.exit = true;  // 終止線程thread    
        thread.join();  
        System.out.println("線程退出!");  
    }
}

Interrupt 方法結束線程

使用 interrupt() 方法來中斷線程有兩種情況:

  1. 線程處於阻塞狀態。如使用了 sleep,同步鎖的 wait,socket 中的 receiver,accept 等方法時,會使線程處於阻塞狀態。使用 interrupt 方法結束線程的時候,一定要先捕獲 InterruptedException 異常之後通過 break 來跳出循環,才能正常結束 run 方法。
public class ThreadInterrupt extends Thread {  
    public void run()  {  
        try {  
            sleep(50000);  // 延遲50秒  
        }  
        catch (InterruptedException e) {  
            System.out.println(e.getMessage());  
        }  
    }  
    public static void main(String[] args) throws Exception  {  
        Thread thread = new ThreadInterrupt();  
        thread.start();  
        System.out.println("在50秒之內按任意鍵中斷線程!");  
        System.in.read();  
        thread.interrupt();  
        thread.join();  
        System.out.println("線程已經退出!");  
    }  
}
  1. 線程未處於阻塞狀態。使用 isInterrupted() 判斷線程的中斷標誌來退出循環。當使用 interrupt() 方法時,中斷標誌就會置 true,和使用自定義的標誌來控制循環是一樣的道理。
public class ThreadSafe extends Thread {
    public void run() {
        while (!isInterrupted()) { //非阻塞過程中通過判斷中斷標誌來退出
            try {
                Thread.sleep(5*1000);//阻塞過程捕獲中斷異常來退出
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;//捕獲到異常之後,執行 break 跳出循環
            }
        }
    }
}

stop 方法終止線程

使用 stop 方法可以強行終止正在運行或掛起的線程。我們可以使用如下的代碼來終止線程:

thread.stop();

採用 stop 是不安全的,主要影響點如下:

  1. thread.stop() 調用之後,創建子線程的線程就會拋出 ThreadDeatherror 的錯誤;
  2. 調用 stop 會釋放子線程所持有的所有鎖。導致了該線程所持有的所有鎖的突然釋放(不可控制),那麼被保護數據就有可能呈現不一致性。

總結

  • 線程創建:推薦使用 Runnable 或者 Callable 方式創建線程,相比繼承,接口實現可以更加靈活,不會受限於Java的單繼承機制。
  • 線程終止:線程終止推薦使用 標誌位 或 Interrupt 方式終止,stop 方式對線程不安全,易導致數據不一致。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章