Java併發——CompletionService

CompletionService

接口CompletionService的功能是以異步的方式生產新的任務,一邊處理已完成任務的結果,這樣可以將執行任務與處理任務分離開來進行處理。使用submit執行任務,使用take取得已完成的任務,並按照完成這些任務的時間順序處理它們的結果。

接口 Completion Service的結構比較簡潔,僅有一個實現類 Executor Completion Service,該類的構造方法如圖所示。
在這裏插入圖片描述

從構造方法的聲明中可以發現,類 Executor Completion Service需要依賴於 Executor對象,大部分的實現也就是使用線程池 ThreadPoolExecutor對象。

take()方法

public class TestDemo {
    public static void main(String[] args) {
        try {
            //take:獲取並移除下一個已完成任務的Futrue,如果目前不存在這樣的任務,則阻塞。
            ExecutorService executorService = Executors.newCachedThreadPool();
            CompletionService cs = new ExecutorCompletionService(executorService);
            for (int i = 0; i < 10; i++) {
                cs.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        long sleepValue = (int)(Math.random() * 1000);
                        System.out.println("sleep=" + sleepValue + " " + Thread.currentThread().getName());
                        Thread.sleep(sleepValue);
                        return "返回值:" + sleepValue + " " + Thread.currentThread().getName();
                    }
                });
            }
            for (int i = 0; i < 10; i++) {
                System.out.println(cs.take().get());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

從運行結果來看,方法 take()是按任務執行的速度,從快到慢的順序獲得Future對象,因爲打印的時間是從小到大。

sleep=738 pool-1-thread-1
sleep=911 pool-1-thread-8
sleep=467 pool-1-thread-10
sleep=407 pool-1-thread-7
sleep=131 pool-1-thread-3
sleep=79 pool-1-thread-6
sleep=563 pool-1-thread-4
sleep=869 pool-1-thread-5
sleep=545 pool-1-thread-2
sleep=551 pool-1-thread-9
返回值:79 pool-1-thread-6
返回值:131 pool-1-thread-3
返回值:407 pool-1-thread-7
返回值:467 pool-1-thread-10
返回值:545 pool-1-thread-2
返回值:551 pool-1-thread-9
返回值:563 pool-1-thread-4
返回值:738 pool-1-thread-1
返回值:869 pool-1-thread-5
返回值:911 pool-1-thread-8

poll()方法

方法poll()的作用是獲取並移除表示下一個已完成任務的 Future,如果不存在這樣的任務,則返回null,方法pol()無阻
塞的效果:

public class TestDemo2 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        CompletionService cs = new ExecutorCompletionService(executorService);
        for (int i = 0; i < 1; i++) {
            cs.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    Thread.sleep(3000);
                    System.out.println("3秒過後");
                    return "返回值";
                }
            });
        }
        for (int i = 0; i < 1; i++) {
            System.out.println(cs.poll());
        }
    }
}

poll()方法返回null,因爲當前沒有任何已完成任務的Future對象,所以返回爲null。poll()方法不具有阻塞特性。
在這裏插入圖片描述

poll(long timeout, TimeUnit unit)

方法Future pol(long timeout, TimeUnit unit)的作用是等待指定的timeout時間,在timeout時間之內獲取到值時立即向下繼續執行,如果超時也立即向下執行。

public class TestDemo3 {
    public static void main(String[] args) {
        try {
            MyCallableA a =  new MyCallableA();
            MyCallableB b = new MyCallableB();

            Executor executor = Executors.newSingleThreadExecutor();
            CompletionService cs = new ExecutorCompletionService(executor);
            cs.submit(a);
            cs.submit(b);

            for (int i = 0; i < 2; i++) {
                System.out.println("zzzzzzzzzzz" + " " + cs.take());
            }
            System.out.println("main end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MyCallableA implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("MyCallableA begin " + System.currentTimeMillis());
        Thread.sleep(1000);
        System.out.println("MyCallableA end " + System.currentTimeMillis());
        return "returnA";
    }
}

class MyCallableB implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("MyCallableB begin " + System.currentTimeMillis());
        Thread.sleep(5000);
        int i = 0;
        if (i == 0) {
            throw new Exception("拋出異常!");
        }
        System.out.println("MyCallableB end " + System.currentTimeMillis());
        return "returnB";
    }
}

MyCallableB雖然出現異常,但是並沒有調用FutureTask的get()方法,所以不出現異常。
在這裏插入圖片描述

調用get()方法:
在這裏插入圖片描述
在這裏插入圖片描述

調換A和B的執行順序:
在這裏插入圖片描述
任務B出現異常,任務A並未輸出。
在這裏插入圖片描述

調用poll()方法:

public class TestDemo3 {
    public static void main(String[] args) {
        try {
            MyCallableA a =  new MyCallableA();
            MyCallableB b = new MyCallableB();

            Executor executor = Executors.newSingleThreadExecutor();
            CompletionService cs = new ExecutorCompletionService(executor);
            cs.submit(a);
            cs.submit(b);

            for (int i = 0; i < 2; i++) {
                System.out.println("zzzzzzzzzzz" + " " + cs.poll());
            }
            Thread.sleep(6000);
            System.out.println("A處" + " " + cs.poll());
            System.out.println("B處" + " " + cs.poll());
            System.out.println("main end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MyCallableA implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("MyCallableA begin " + System.currentTimeMillis());
        Thread.sleep(1000);
        System.out.println("MyCallableA end " + System.currentTimeMillis());
        return "returnA";
    }
}

class MyCallableB implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("MyCallableB begin " + System.currentTimeMillis());
        Thread.sleep(5000);
        int i = 0;
        if (i == 0) {
            throw new Exception("拋出異常!");
        }
        System.out.println("MyCallableB end " + System.currentTimeMillis());
        return "returnB";
    }
}

任務B出現異常後,poll()返回值爲null
在這裏插入圖片描述

poll()後調用get()方法獲取返回值:
在這裏插入圖片描述
A任務返回值正常,任務B返回值出現異常。
在這裏插入圖片描述

調換A和B任務執行順序:
在這裏插入圖片描述
任務A並未打印,任務B拋出異常。
在這裏插入圖片描述

Future submit(Runnable task, V result)

public class TestDemo4 {
    public static void main(String[] args) {
        UserInfo userInfo = new UserInfo();
        MyRunnable5 myRunnable5 = new MyRunnable5(userInfo);

        Executor executor = Executors.newCachedThreadPool();
        CompletionService cs = new ExecutorCompletionService(executor);
        Future<UserInfo> future = cs.submit(myRunnable5, userInfo);
        try {
            System.out.println(future.get().getUsername() + " " + future.get().getPassword());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class UserInfo {
    private String username;
    private String password;

    public UserInfo() {
        super();
    }

    public UserInfo(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}


class MyRunnable5 implements Runnable {
    private UserInfo userInfo;

    public MyRunnable5(UserInfo userInfo) {
        this.userInfo = userInfo;
    }

    @Override
    public void run() {
        userInfo.setUsername("usernameValue");
        userInfo.setPassword("passwordValue");
        System.out.println("run運行了 ");
    }
}

在這裏插入圖片描述

總結

接口CompletionService完全可以避免FutureTask類阻塞的缺點,可更加有效地處理Future的返回值,也就是哪個任務先執行完,CompletionService 就先取得這個任務的返回值再處理。

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