线程池:CompletionService

接口CompletionService的功能是以异步的方式一边生产新的任务,一边处理已完成任务的结果,这样可以将执行任务与处理任务分离开来进行处理。

使用submit执行任务,使用take取得已完成的任务,并按照完成这些任务的时间顺序处理它们的结果

接口CompletionService完全可以避免FutureTask类阻塞的缺点,可更加有效地处理Future的返回值,也就是哪个任务先执行完,CompletionService就先取得这个任务的返回值再处理。

使用CompletionService解决Future的缺点

使用CompletionService接口中的take()方法,

它的主要作用就是取得Future对象,方法声明结构如下:public Future<V> take() throws InterruptedException

package ExecutorCompletionService_0;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
    private String userName;
    private Long sleepValue;
    public MyCallable(String userName,Long sleepValue){
        super();
        this.userName=userName;
        this.sleepValue=sleepValue;
    }
    @Override
    public String call() throws Exception {
        System.out.println("**"+userName);
        Thread.sleep(sleepValue);
        return "KK "+userName;
    }
}

package ExecutorCompletionService_0;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class Run {
    public static void main(String[] args) {
        try {
            //接口Future具有阻塞同步性,这样的代码运行效率会大打折扣,接口CompletionService可以解决这样的问题
            MyCallable callable=new MyCallable("1",5000L);
            MyCallable callable1=new MyCallable("2",4000L);
            MyCallable callable2=new MyCallable("3",3000L);
            MyCallable callable3=new MyCallable("4",2000L);
            MyCallable callable4=new MyCallable("5",1000L);
            List<Callable> callables=new ArrayList<Callable>();
            callables.add(callable);
            callables.add(callable1);
            callables.add(callable2);
            callables.add(callable3);
            callables.add(callable4);
            ThreadPoolExecutor executor=new ThreadPoolExecutor(5,10,5, TimeUnit.SECONDS,
                    new LinkedBlockingDeque<Runnable>());
            CompletionService service=new ExecutorCompletionService(executor);
            for (int i=0;i<5;i++){
                service.submit(callables.get(i));
            }
            for (int i=0;i<5;i++){
                String str= (String) service.take().get();
                System.out.println(str);
                // 从打印的结果来看,CompletionService完全解决了Future阻塞的特性,
                // 也就是使用CompletionService接口后,哪个任务先执行完,哪个任务的返回值就先打印。
                // 在CompletionService接口中如果当前没有任务被执行完,则csRef. take().get()方法还是呈阻塞特性。
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
View Code

使用take()方法

方法take()取得最先完成任务的Future对象,谁执行时间最短谁最先返回。

package ExecutorCompletionService_1;
import java.util.concurrent.*;
public class Run {
    public static void main(String[] args) {
        try {
            //方法take()取得最先完成任务的Future对象,谁执行时间最短谁最先返回。
            ExecutorService service=Executors.newCachedThreadPool();
            CompletionService completionService=new ExecutorCompletionService(service);
            for (int i=0;i<10;i++){
                service.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        Thread.sleep((long) Math.random()*1000);
                        return Thread.currentThread().getName();
                    }
                });
            }
            for (int i=0;i<10;i++){
                System.out.println(completionService.take().get());
                //从运行结果来看,方法take()是按任务执行的速度,
                //从快到慢的顺序获得Future对象,因为打印的时间是从小到大。
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


    }
}
View Code

使用poll()方法

方法poll()的作用是获取并移除表示下一个已完成任务的Future,如果不存在这样的任务,则返回null,方法poll()无阻塞的效果。

package ExecutorCompletionService_1;
import java.util.concurrent.*;
public class Run {
    public static void main(String[] args) {
        try {
            //方法poll()的作用是获取并移除表示下一个已完成任务的Future,
            // 如果不存在这样的任务,则返回null,方法poll()无阻塞的效果。
            ExecutorService service=Executors.newCachedThreadPool();
            CompletionService completionService=new ExecutorCompletionService(service);
            for (int i=0;i<10;i++){
                service.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        Thread.sleep((long) Math.random()*1000);
                        return Thread.currentThread().getName();
                    }
                });
            }
            for (int i=0;i<10;i++){
                System.out.println(completionService.poll()!=null);
                //方法poll()返回的Future为null,因为当前没有任何已完成任务的Future对象,所以返回为null,
                // 通过此实验证明poll()方法不像take()方法具有阻塞的效果。
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

使用poll(long timeout, TimeUnit unit)方法

方法Future<V> poll(long timeout, TimeUnit unit)的作用是等待指定的timeout时间,在timeout时间之内获取到值时立即向下继续执行,如果超时也立即向下执行。

方法poll()中timeout的值如果小于任务执行的时间,则返回值就是null。

类CompletionService与异常

使用CompletionService执行任务的过程中不可避免会出现各种情况的异常,下面来实验一下CompletionService对异常处理的流程。

虽然MyCallableB.java出现异常,但并没有调用FutureTask类的get()方法,所以不出现异常。

调用get方法后就会捕获到callable中抛出的异常

 

 

posted @ 2020-05-24 10:43  弱水三千12138  阅读(263)  评论(0)    收藏  举报