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

本文開始將開始介紹 Java 多線程與併發相關的知識,多謝各位一直以來的關注與支持。關注個人公衆號「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() 方法將做爲線程的執行體,同時容許有返回值。socket

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

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() 方法來中斷線程有兩種狀況:code

  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 方式對線程不安全,易致使數據不一致。
相關文章
相關標籤/搜索