java多線程:ExecutorService方法詳解

 1.ExecutorService是java多線程併發包下比較底層也是比較重要的接口,瞭解該接口中的方法的和每個方法的作用是非常有必要的,那麼我們現在就來看看每個方法的具體作用。

如圖我們可以看到ExecutorService的所有方法:

接下來對每個方法做具體的瞭解。其中shutdown(),shutdownNow(),isShutDown(),submit()相關方法前面已經介紹過,這裏不做重複工作,想了解請移步https://blog.csdn.net/zhaoliubao1/article/details/102939488https://blog.csdn.net/zhaoliubao1/article/details/103120089做詳細瞭解。本文只對invoke*()相關方法做了解。

 

2.invokeAny(Collection<? extends Callable<T>> tasks);

方法:

源碼翻譯:Executes the given tasks, returning the result of one that has completed successfully (i.e., without throwin an exception), if any do. Upon normal or exceptional return, tasks that have not completed are cancelled.The results of this method are undefined if the given collection is modified while this operation is in progress.(執行給定的任務,返回第一個已經執行完的結果(如果沒有拋出異常【這裏是指的是程序員手動的拋出異常,而不是調用這invokeAny時發生異常】。正常或者異常返回【這裏的異常指的是調用invokeAny時本身發生了異常】,那麼那些未完成的任務將會被取消。但是在執行過程中tasks如果被改變那麼返回的結果將不確定。))

《java併發編程:核心框架與核心方法》:invokeAny取得第一個完成的任務的結果值,當第一個任務執行完成後,會調用interrupt()方法來將其他任務中斷。【個人覺得這個描述相當精髓,精髓之處在於他提到了使用interrupt()去中斷其他任務,如果沒有提到這個那麼下面的試驗結果將會與源碼中的解釋相悖,至此我們對interrupt的工作機制瞭解就是非常重要的了,瞭解interruptd方法的工作機制移步到:https://blog.csdn.net/zhaoliubao1/article/details/102385808

但是:

                                            (圖片來源:《java併發編程:核心框架與核心方法》) 

會出現上敘述的兩種情況。

接下來對上述論述進行試驗論證:

 

package com.springboot.thread.executorService;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestExecutorService {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        MyCallableA myCallableA = new MyCallableA();
        MyCallableB myCallableB = new MyCallableB();
        List<Callable<String>> callableList = new ArrayList<>();
        callableList.add(myCallableA);
        callableList.add(myCallableB);
        String result = executor.invokeAny(callableList);
        System.out.println(result);
    }
}

class MyCallableA implements Callable<String>{

    @Override
    public String call() throws Exception {
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = dateformat.format(System.currentTimeMillis());
        System.out.println("MyCallableA begin at "+dateStr);
        //thread do sth.
        Thread.sleep(3000);
        System.out.println("MyCallableA end at "+dateStr);
        return "MyCallableA";
    }
}

class  MyCallableB implements Callable<String>{

    @Override
    public String call() throws Exception {
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = dateformat.format(System.currentTimeMillis());
        System.out.println("MyCallableB begin at "+ dateStr);
        //thread do sth.
        Thread.sleep(5000);
        System.out.println("MyCallableB end at "+dateStr);
        return "MyCallableB";
    }
}

運行結果:

另一種情況:

 

package com.springboot.thread.executorService;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestExecutorService {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        MyCallableA myCallableA = new MyCallableA();
        MyCallableB myCallableB = new MyCallableB();
        List<Callable<String>> callableList = new ArrayList<>();
        callableList.add(myCallableA);
        callableList.add(myCallableB);
        String result = executor.invokeAny(callableList);
        System.out.println(result);
    }
}

class MyCallableA implements Callable<String>{

    @Override
    public String call() throws Exception {
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = dateformat.format(System.currentTimeMillis());
        System.out.println("MyCallableA begin at "+dateStr);
        //thread do sth.
        //Thread.sleep(3000);
        for (int i=0;i<30;i++){
            Math.random();
            Math.random();
            Math.random();
            System.out.println("MyCallableA "+(i+1));
        }
        System.out.println("MyCallableA end at "+dateStr);
        return "MyCallableA";
    }
}

class  MyCallableB implements Callable<String>{

    @Override
    public String call() throws Exception {
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = dateformat.format(System.currentTimeMillis());
        System.out.println("MyCallableB begin at "+ dateStr);
        //thread do sth.
        //Thread.sleep(5000);
        for (int i =0; i<50;i++){
            Math.random();
            Math.random();
            Math.random();
            System.out.println("MyCallableB "+(i+1));
        }
        System.out.println("MyCallableB end at "+dateStr);
        return "MyCallableB";
    }
}

運行結果:


MyCallableA begin at 2019-11-30 16:43:18
MyCallableA 1
MyCallableA 2
MyCallableA 3
MyCallableA 4
MyCallableA 5
MyCallableA 6
MyCallableA 7
MyCallableA 8
MyCallableA 9
MyCallableA 10
MyCallableA 11
MyCallableA 12
MyCallableA 13
MyCallableA 14
MyCallableA 15
MyCallableA 16
MyCallableA 17
MyCallableA 18
MyCallableA 19
MyCallableA 20
MyCallableA 21
MyCallableA 22
MyCallableA 23
MyCallableA 24
MyCallableA 25
MyCallableA 26
MyCallableA 27
MyCallableA 28
MyCallableA 29
MyCallableA 30
MyCallableA end at 2019-11-30 16:43:18
MyCallableB begin at 2019-11-30 16:43:18
MyCallableB 1
MyCallableB 2
MyCallableB 3
MyCallableB 4
MyCallableB 5
-------------------------
MyCallableA (MyCallableA 結束的標誌)
-------------------------
MyCallableB 6
MyCallableB 7
MyCallableB 8
MyCallableB 9
MyCallableB 10
MyCallableB 11
MyCallableB 12
MyCallableB 13
MyCallableB 14
MyCallableB 15
MyCallableB 16
MyCallableB 17
MyCallableB 18
MyCallableB 19
MyCallableB 20
MyCallableB 21
MyCallableB 22
MyCallableB 23
MyCallableB 24
MyCallableB 25
MyCallableB 26
MyCallableB 27
MyCallableB 28
MyCallableB 29
MyCallableB 30
MyCallableB 31
MyCallableB 32
MyCallableB 33
MyCallableB 34
MyCallableB 35
MyCallableB 36
MyCallableB 37
MyCallableB 38
MyCallableB 39
MyCallableB 40
MyCallableB 41
MyCallableB 42
MyCallableB 43
MyCallableB 44
MyCallableB 45
MyCallableB 46
MyCallableB 47
MyCallableB 48
MyCallableB 49
MyCallableB 50
MyCallableB end at 2019-11-30 16:43:18

上述的結果正是我所述的與源碼中的解釋相悖(看起來相悖)的運行結果。

 

手動拋出異常的試驗:

package com.springboot.thread.executorService;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestExecutorService {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        MyCallableA myCallableA = new MyCallableA();
        MyCallableB myCallableB = new MyCallableB();
        List<Callable<String>> callableList = new ArrayList<>();
        callableList.add(myCallableA);
        callableList.add(myCallableB);
        String result = executor.invokeAny(callableList);
        System.out.println(result);
    }
}

class MyCallableA implements Callable<String>{

    @Override
    public String call() throws Exception {
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = dateformat.format(System.currentTimeMillis());
        System.out.println("MyCallableA begin at "+dateStr);
        //thread do sth.
        //Thread.sleep(3000);
        for (int i=0;i<30;i++){
            Math.random();
            Math.random();
            Math.random();
            System.out.println("MyCallableA is running..."+(i+1));
        }
        if(1==1){
            System.out.println("MyCallableA occurred exception...");
            throw new NullPointerException("MyCallableA occurred exception...");
        }
        System.out.println("MyCallableA end at "+dateStr);
        return "MyCallableA";
    }
}

class  MyCallableB implements Callable<String>{

    @Override
    public String call() throws Exception {
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = dateformat.format(System.currentTimeMillis());
        System.out.println("MyCallableB begin at "+ dateStr);
        //thread do sth.
        //Thread.sleep(5000);
        for (int i =0; i<50;i++){
            Math.random();
            Math.random();
            Math.random();
            System.out.println("MyCallableB is running..."+(i+1));
        }
        System.out.println("MyCallableB end at "+dateStr);
        return "MyCallableB";
    }
}

運行結果:

MyCallableA begin at 2019-12-02 10:38:03
MyCallableB begin at 2019-12-02 10:38:03
MyCallableB is running...1
MyCallableB is running...2
MyCallableA is running...1
MyCallableB is running...3
MyCallableA is running...2
MyCallableB is running...4
MyCallableA is running...3
MyCallableB is running...5
MyCallableA is running...4
MyCallableB is running...6
MyCallableA is running...5
MyCallableA is running...6
MyCallableA is running...7
MyCallableB is running...7
MyCallableA is running...8
MyCallableA is running...9
MyCallableA is running...10
MyCallableB is running...8
MyCallableA is running...11
MyCallableB is running...9
MyCallableB is running...10
MyCallableB is running...11
MyCallableB is running...12
MyCallableB is running...13
MyCallableB is running...14
MyCallableB is running...15
MyCallableB is running...16
MyCallableB is running...17
MyCallableB is running...18
MyCallableA is running...12
MyCallableB is running...19
MyCallableA is running...13
MyCallableB is running...20
MyCallableA is running...14
MyCallableB is running...21
MyCallableA is running...15
MyCallableB is running...22
MyCallableA is running...16
MyCallableB is running...23
MyCallableA is running...17
MyCallableB is running...24
MyCallableA is running...18
MyCallableB is running...25
MyCallableA is running...19
MyCallableB is running...26
MyCallableA is running...20
MyCallableB is running...27
MyCallableA is running...21
MyCallableA is running...22
MyCallableA is running...23
MyCallableA is running...24
MyCallableA is running...25
MyCallableA is running...26
MyCallableA is running...27
MyCallableA is running...28
MyCallableB is running...28
MyCallableA is running...29
MyCallableA is running...30
--------------------------------------------------------
MyCallableA occurred exception...(MyCallableA 拋出異常)
--------------------------------------------------------
MyCallableB is running...29
MyCallableB is running...30
MyCallableB is running...31
MyCallableB is running...32
MyCallableB is running...33
MyCallableB is running...34
MyCallableB is running...35
MyCallableB is running...36
MyCallableB is running...37
MyCallableB is running...38
MyCallableB is running...39
MyCallableB is running...40
MyCallableB is running...41
MyCallableB is running...42
MyCallableB is running...43
MyCallableB is running...44
MyCallableB is running...45
MyCallableB is running...46
MyCallableB is running...47
MyCallableB is running...48
MyCallableB is running...49
MyCallableB is running...50
MyCallableB end at 2019-12-02 10:38:03
------------------------------------------
MyCallableB (MyCallableB 運行結束的標誌)
------------------------------------------

從運行結果來看:理論上講,MyCallableA 應該先於MyCallableB運行完,調用invokeAny()方法時會取得第一個運行完成的結果,但是由於在MyCallableA在運行過程中,出現了異常導致MyCallableA停止,但是並不拋出異常自己處理了,所以MyCallableB繼續執行然後返回。使用Try-Catch顯示拋出捕獲異常會有什麼樣的結果呢?

例子如下:

package com.springboot.thread.executorService;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestExecutorService {
    public static void main(String[] args) throws Exception {
        try {
            ExecutorService executor = Executors.newCachedThreadPool();
            MyCallableA myCallableA = new MyCallableA();
            MyCallableB myCallableB = new MyCallableB();
            List<Callable<String>> callableList = new ArrayList<>();
            callableList.add(myCallableA);
            callableList.add(myCallableB);
            String result = executor.invokeAny(callableList);
            System.out.println(result);
        }catch (Exception e){
            System.out.println(e.getMessage());
            //throw e;
        }

    }
}

class MyCallableA implements Callable<String>{

    @Override
    public String call() throws Exception {
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = dateformat.format(System.currentTimeMillis());
        System.out.println("MyCallableA begin at "+dateStr);
        //thread do sth.
        //Thread.sleep(3000);
        try{
            for (int i=0;i<30;i++){
                Math.random();
                Math.random();
                Math.random();
                System.out.println("MyCallableA is running..."+(i+1));
            }
            if(1==1){
                System.out.println("MyCallableA occurred exception...");
                throw new NullPointerException("MyCallableA occurred exception...");
            }
            System.out.println("MyCallableA end at "+dateStr);

        }catch (Exception e){
           //throw  new Exception("MyCallableA occurred exception  caused the program to stop....");
            throw e;
        }

        return "MyCallableA";
    }
}

class  MyCallableB implements Callable<String>{

    @Override
    public String call() throws Exception {
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = dateformat.format(System.currentTimeMillis());
        System.out.println("MyCallableB begin at "+ dateStr);
        //thread do sth.
        //Thread.sleep(5000);
        for (int i =0; i<50;i++){
            Math.random();
            Math.random();
            Math.random();
            System.out.println("MyCallableB is running..."+(i+1));
        }
        System.out.println("MyCallableB end at "+dateStr);
        return "MyCallableB";
    }
}

運行結果:

MyCallableA begin at 2019-12-02 13:49:31
MyCallableA is running...1
MyCallableA is running...2
MyCallableA is running...3
MyCallableA is running...4
MyCallableA is running...5
MyCallableA is running...6
MyCallableA is running...7
MyCallableA is running...8
MyCallableA is running...9
MyCallableA is running...10
MyCallableA is running...11
MyCallableA is running...12
MyCallableA is running...13
MyCallableA is running...14
MyCallableA is running...15
MyCallableA is running...16
MyCallableA is running...17
MyCallableA is running...18
MyCallableA is running...19
MyCallableA is running...20
MyCallableA is running...21
MyCallableA is running...22
MyCallableA is running...23
MyCallableA is running...24
MyCallableA is running...25
MyCallableA is running...26
MyCallableA is running...27
MyCallableA is running...28
MyCallableA is running...29
MyCallableA is running...30
MyCallableA occurred exception...
MyCallableB begin at 2019-12-02 13:49:31
MyCallableB is running...1
MyCallableB is running...2
MyCallableB is running...3
MyCallableB is running...4
MyCallableB is running...5
MyCallableB is running...6
MyCallableB is running...7
MyCallableB is running...8
MyCallableB is running...9
MyCallableB is running...10
MyCallableB is running...11
MyCallableB is running...12
MyCallableB is running...13
MyCallableB is running...14
MyCallableB is running...15
MyCallableB is running...16
MyCallableB is running...17
MyCallableB is running...18
MyCallableB is running...19
MyCallableB is running...20
MyCallableB is running...21
MyCallableB is running...22
MyCallableB is running...23
MyCallableB is running...24
MyCallableB is running...25
MyCallableB is running...26
MyCallableB is running...27
MyCallableB is running...28
MyCallableB is running...29
MyCallableB is running...30
MyCallableB is running...31
MyCallableB is running...32
MyCallableB is running...33
MyCallableB is running...34
MyCallableB is running...35
MyCallableB is running...36
MyCallableB is running...37
MyCallableB is running...38
MyCallableB is running...39
MyCallableB is running...40
MyCallableB is running...41
MyCallableB is running...42
MyCallableB is running...43
MyCallableB is running...44
MyCallableB is running...45
MyCallableB is running...46
MyCallableB is running...47
MyCallableB is running...48
MyCallableB is running...49
MyCallableB is running...50
MyCallableB end at 2019-12-02 13:49:31
MyCallableB

從運行結果來看,就算我們顯式的拋出異常,主線程還是不捕獲異常,也就說明子線程出現異常事不影響主線程的運行。

 

當所有任務全部發生異常時:

package com.springboot.thread.executorService;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestExecutorService {
    public static void main(String[] args) throws Exception {
        try {
            ExecutorService executor = Executors.newCachedThreadPool();
            MyCallableA myCallableA = new MyCallableA();
            MyCallableB myCallableB = new MyCallableB();
            List<Callable<String>> callableList = new ArrayList<>();
            callableList.add(myCallableA);
            callableList.add(myCallableB);
            String result = executor.invokeAny(callableList);
            System.out.println(result);
        }catch (Exception e){
            System.out.println(e.getMessage());
            //throw e;
        }

    }
}

class MyCallableA implements Callable<String>{

    @Override
    public String call() throws Exception {
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = dateformat.format(System.currentTimeMillis());
        System.out.println("MyCallableA begin at "+dateStr);
        //thread do sth.
        //Thread.sleep(3000);
        try{
            for (int i=0;i<30;i++){
                Math.random();
                Math.random();
                Math.random();
                System.out.println("MyCallableA is running..."+(i+1));
            }
            if(1==1){
                System.out.println("MyCallableA occurred exception...");
                throw new NullPointerException("MyCallableA occurred exception...");
            }
            //final  int i = 1 / 0;
            System.out.println("MyCallableA end at "+dateStr);

        }catch (Exception e){
           //throw  new Exception("MyCallableA occurred exception  caused the program to stop....");
            throw e;
        }

        return "MyCallableA";
    }
}

class  MyCallableB implements Callable<String>{

    @Override
    public String call() throws Exception {
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = dateformat.format(System.currentTimeMillis());
        System.out.println("MyCallableB begin at "+ dateStr);
        //thread do sth.
        //Thread.sleep(5000);
        for (int i =0; i<50;i++){
            Math.random();
            Math.random();
            Math.random();
            System.out.println("MyCallableB is running..."+(i+1));
        }
        final  int i = 1 / 0;
        System.out.println("MyCallableB end at "+dateStr);
        return "MyCallableB";
    }
}

運行結果:

MyCallableA begin at 2019-12-02 14:46:46
MyCallableB begin at 2019-12-02 14:46:46
MyCallableA is running...1
MyCallableA is running...2
MyCallableA is running...3
MyCallableA is running...4
MyCallableA is running...5
MyCallableA is running...6
MyCallableA is running...7
MyCallableA is running...8
MyCallableA is running...9
MyCallableA is running...10
MyCallableA is running...11
MyCallableA is running...12
MyCallableA is running...13
MyCallableA is running...14
MyCallableA is running...15
MyCallableA is running...16
MyCallableB is running...1
MyCallableA is running...17
MyCallableB is running...2
MyCallableA is running...18
MyCallableA is running...19
MyCallableA is running...20
MyCallableA is running...21
MyCallableA is running...22
MyCallableA is running...23
MyCallableA is running...24
MyCallableA is running...25
MyCallableA is running...26
MyCallableA is running...27
MyCallableA is running...28
MyCallableA is running...29
MyCallableA is running...30
MyCallableA occurred exception...
MyCallableB is running...3
MyCallableB is running...4
MyCallableB is running...5
MyCallableB is running...6
MyCallableB is running...7
MyCallableB is running...8
MyCallableB is running...9
MyCallableB is running...10
MyCallableB is running...11
MyCallableB is running...12
MyCallableB is running...13
MyCallableB is running...14
MyCallableB is running...15
MyCallableB is running...16
MyCallableB is running...17
MyCallableB is running...18
MyCallableB is running...19
MyCallableB is running...20
MyCallableB is running...21
MyCallableB is running...22
MyCallableB is running...23
MyCallableB is running...24
MyCallableB is running...25
MyCallableB is running...26
MyCallableB is running...27
MyCallableB is running...28
MyCallableB is running...29
MyCallableB is running...30
MyCallableB is running...31
MyCallableB is running...32
MyCallableB is running...33
MyCallableB is running...34
MyCallableB is running...35
MyCallableB is running...36
MyCallableB is running...37
MyCallableB is running...38
MyCallableB is running...39
MyCallableB is running...40
MyCallableB is running...41
MyCallableB is running...42
MyCallableB is running...43
MyCallableB is running...44
MyCallableB is running...45
MyCallableB is running...46
MyCallableB is running...47
MyCallableB is running...48
MyCallableB is running...49
MyCallableB is running...50
java.lang.ArithmeticException: / by zero

當所有任務都發生異常時會拋出左後一個任務的異常,並使整個應用程序停止。

總結一下:使用invokeAny()方法時如果都沒有發生異常那麼會運行完所有任務並取第一個任務的結果,但是如果有一個任務有異常會取下一個任務的結果,拋出的異常不會被主線程捕獲到,全部發生異常時主線程會捕獲到最後一個任務的異常。超時時拋:

TimeOutException

(3)invokeAll()方法:invokeAll()方法會返回所有任務的執行結果,此方法的執行效果爲阻塞執行,要把所有的結果都取回時再繼續向下執行。只要有一個任務發生異常就拋出異常。快的任務沒有異常的話可以返回。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章