Java 併發系列之一

Java 併發系列之一

簡單的總結了一些 Java 經常使用的集合以後,發現許多集合都針對多線程提供了支持,好比 ConcurrentHashMap 使用分段鎖來提升多線程環境下的性能表現與安全表現。因此我打算接着對 Java 併發的相關內容作一個簡單總結。java

線程與進程

進程是操做系統分配資源的基本單位,也就是說進程是運行中的程序。安全

線程是進程中的基本執行單元,每一個進程都至少擁有一個線程。線程不獨立擁有操做系統資源,線程共享進程的操做系統資源。多線程

處理併發問題爲何使用多線程而不是多進程,在我看來主要有兩點。一是進程間通訊難度大於線程間通訊,會增長開發難度,二是線程間切換效率高於進程間切換,選擇多線程更適合併發場景。併發

線程的生命週期

這裏咱們只簡單介紹線程 new->Runnable->Running->Dead 這個流程,不考慮 Block 的狀況。ide

  1. 當咱們建立一個 Thread 對象時,這個線程就進入了 new 的狀態。
  2. 當時機成熟,咱們調用這個對象的 start() 方法時,這個線程就進入了 Runnable 的狀態。
  3. 而後這個線程就會等待 CPU 資源,若是獲取到 CPU 資源就會自動運行。
  4. 運行結束後線程就會進入到 Dead 狀態。

須要注意的是一個 Thread 對象只有一次調用 start() 方法的機會,不管這個線程是否順利執行結束。函數

建立線程

建立線程也就是線程生命週期中的 new 狀態。在 Java 中建立線程有 3 中方式:性能

  1. 繼承 Thread 類並重寫 run 方法
  2. 實現 Runnable 接口
  3. 實現 Callable 接口

繼承 Thread 類來建立線程

使用繼承 Thread 的方式能夠直接在 run 方法中利用 this 來獲取當前線程的信息,而不須要經過 Thread 類的靜態方法 currentThread() 方法來獲取當前的線程。this

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(this.getName()+"正在運行");
    }
}

public static void main(String[] args) {
    Thread thread = new MyThread();
    thread.start();
}
//輸出結果:Thread-0 正在運行

若是想要快速實現一個匿名的類來執行某個簡單操做的話,能夠用下面的方式:操作系統

public static void main(String[] args) {
    new Thread() {
        @Override
        public void run() {
            System.out.println(this.getName() + "正在執行");
        }
    }.start();
}
//輸出結果:Thread-0 正在運行

實現 Runnable 接口來建立線程

因爲 Java 是不支持多繼承的,因此若是要繼承 Thread 類之外的類,使用 Runnable 來實現或許是個不錯的選擇。線程

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"正在運行");
    }
}

public static void main(String[] args) {
    Runnable runnable = new MyRunnable();
    Thread thread = new Thread(runnable);
    thread.start();
}
//輸出結果:Thread-0 正在運行

其實 Runnable 接口只包含一個 run 方法,本質上仍是經過重寫 Thread 類的 run 方法來達到建立線程的目的。Runnable 仍是一個函數式接口,因此想要聲明一個匿名的 Thread 類還能經過下面的方式:

public static void main(String[] args) {
    new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + "正在運行");
    }).start();
}
//輸出結果:Thread-0 正在運行

實現 Callable 接口來建立線程

上面兩種建立線程的方式都是不支持返回值的,若是須要線程在執行以後提供返回值,就能夠經過 Callable 來建立線程。使用 Callable 建立線程分爲如下幾個步驟:

  1. 建立類實現 Callable 接口的 call() 方法
  2. 使用 FutureTask 來包裝上一步建立的類
  3. 使用 FutureTask 來建立 Thread 類
  4. 使用 FutureTask 對象的 get() 方法來獲取返回值
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"正在運行");
        return 316495132;
    }
}

public static void main(String[] args) 
    throws ExecutionException,InterruptedException {
    Callable callable = new MyCallable();
    FutureTask<Integer> futureTask = new FutureTask<>(callable);
    Thread thread = new Thread(futureTask);
    thread.start();
    System.out.println("返回值爲:" + futureTask.get());
}
//輸出結果:
//Thread-0 正在運行
//返回值爲:316495132

小結

  1. 若是沒有特殊的需求,實現 Runnable 接口或許是一個比較好的選擇
  2. 若是須要線程執行完成後提供返回值,就只能選擇繼承 Callable 接口
相關文章
相關標籤/搜索