使用CompletionService批处理任务

    
分享一篇关于Java中ExecutorService和CompletionService区别,有需要的朋友可以参考一下。

我们现在在Java中使用多线程通常不会直接用Thread对象了,而是会用到java.util.concurrent包下的ExecutorService类来初始化一个线程池供我们使用。

之前我一直习惯自己维护一个list保存submit的callable task所返回的Future对象。

在主线程中遍历这个list并调用Future的get()方法取到Task的返回值。

 

但是,我在很多地方会看到一些代码通过CompletionService包装ExecutorService,然后调用其take()方法去取Future对象。以前没研究过这两者之间的区别。

今天看了源代码之后就明白了。

 

这两者最主要的区别在于submit的task不一定是按照加入自己维护的list顺序完成的。

从list中遍历的每个Future对象并不一定处于完成状态,这时调用get()方法就会被阻塞住,如果系统是设计成每个线程完成后就能根据其结果继续做后面的事,这样对于处于list后面的但是先完成的线程就会增加了额外的等待时间。

而CompletionService的实现是维护一个保存Future对象的BlockingQueue。只有当这个Future对象状态是结束的时候,才会加入到这个Queue中,take()方法其实就是Producer-Consumer中的Consumer。它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中。

所以,先完成的必定先被取出。这样就减少了不必要的等待时间。


import java.util.Random;  
import java.util.concurrent.BlockingQueue;  
import java.util.concurrent.Callable;  
import java.util.concurrent.CompletionService;  
import java.util.concurrent.ExecutionException;  
import java.util.concurrent.ExecutorCompletionService;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.Future;  
import java.util.concurrent.LinkedBlockingQueue;  
  
public class Test17 {  
    public static void main(String[] args) throws Exception {  
        Test17 t = new Test17();  
        t.count1();  
        t.count2();  
    }  
//使用阻塞容器保存每次Executor处理的结果,在后面进行统一处理  
    public void count1() throws Exception{  
        ExecutorService exec = Executors.newCachedThreadPool();  
        BlockingQueue<Future<Integer>> queue = new LinkedBlockingQueue<Future<Integer>>();  
        for(int i=0; i<10; i++){  
            Future<Integer> future =exec.submit(getTask());  
            queue.add(future);  
        }  
        int sum = 0;  
        int queueSize = queue.size();  
        for(int i=0; i<queueSize; i++){  
            sum += queue.take().get();  
        }  
        System.out.println("总数为:"+sum);  
        exec.shutdown();  
    }  
//使用CompletionService(完成服务)保持Executor处理的结果  
    public void count2() throws InterruptedException, ExecutionException{  
        ExecutorService exec = Executors.newCachedThreadPool();  
        CompletionService<Integer> execcomp = new ExecutorCompletionService<Integer>(exec); // 线程池外面封装一层CompletionService 
        for(int i=0; i<10; i++){  
            execcomp.submit(getTask());  
        }  
        int sum = 0;  
        for(int i=0; i<10; i++){  
//检索并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则等待。  
            Future<Integer> future = execcomp.take();  
            sum += future.get();  
        }  
        System.out.println("总数为:"+sum);  
        exec.shutdown();  
    }  
    //得到一个任务  
    public Callable<Integer> getTask(){  
        final Random rand = new Random();  
        Callable<Integer> task = new Callable<Integer>(){  
            @Override  
            public Integer call() throws Exception {  
                int i = rand.nextInt(10);  
                int j = rand.nextInt(10);  
                int sum = i*j;  
                System.out.print(sum+"\t");  
                return sum;  
            }  
        };  
        return task;  
    }  
    /** 
     * 执行结果: 
        6   6   14  40  40  0   4   7   0   0   总数为:106 
        12  6   12  54  81  18  14  35  45  35  总数为:312 
     */  
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值