有关Java 5.0+ 并发包的探讨-2 section -补充-1

本文通过两个示例对比,详细解析了CompletionService如何确保异步任务的结果能够按照原始提交顺序获取。探讨了其内部实现机制,包括构造函数、submit方法及QueueingFuture类的作用。

我们现在来看看CompletionService

package ExecutorDemos;

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;

public class ExecutorBasicDemo {

	public static void main(String[] args) throws InterruptedException,
			ExecutionException {
		//创建ExecutorCompletionService实例
		ExecutorService service=Executors.newFixedThreadPool(20);
		CompletionService<Object> serv = new ExecutorCompletionService<Object>(service);

		//创建50个工作
		for (int index = 0; index < 50; index++) {
			final int Position = index;  
			Callable<Object> dosomethings = new Callable<Object>() {
				public Object call() throws Exception {
					
					long value=(long) (Math.random() * 10000);
					System.out.println("Current sleep time is:"+value+", Position is "+Position);
					Thread.sleep(value);
					return value;
				}
			};
			serv.submit(dosomethings);
		}

		Thread.sleep(1000);
		for (int index = 0; index < 50; index++) {
			//按顺序取出来
			Future<Object> task = serv.take();
			Object value = task.get();
			System.out.println(value);
		}
		service.shutdown();
	}

}


执行的结果是:

 

 


你发现结果是按顺序取出结果的,而且可以看出它与Future有关系。在上一篇中,我们看过一个例子:

package ExecutorDemos;

 import java.util.ArrayList;  
 import java.util.List;  
 import java.util.Random;  
 import java.util.concurrent.*;  
 import java.util.concurrent.atomic.AtomicInteger;  
  
 public class FutureTaskTest {  
   
     private static AtomicInteger Count = new AtomicInteger(0);  
  
     @SuppressWarnings("unchecked")  
     public static void main(String args[]) {  
  
       ExecutorService es = Executors.newFixedThreadPool(10);  
         List<Future<Integer>> tasks = new ArrayList<Future<Integer>>();  
        for (int i = 0; i <= 10; i++) {  
        	final int iCount=i;
            FutureTask<Integer> futureTask = new FutureTask<Integer>(  
                     new Callable() {  
                       @SuppressWarnings("static-access")  
                         public Integer call() throws Exception {  
                             Thread.currentThread().sleep(  
                                     (new Random()).nextInt(1000));  
                             System.out.println("Postion is "+iCount);
                            return Count.getAndIncrement();  
                         }  
                     });  
             tasks.add(futureTask);  
             es.submit(futureTask);  
         }  
 
         Integer result = new Integer(0);  
        try {  
             for (Future<Integer> task : tasks) {  
                 result += (Integer) task.get();  
            }  
            es.shutdown();  
             System.out.println(result);  
        } catch (InterruptedException e) {  
             e.printStackTrace();  
        } catch (ExecutionException e) {  
            e.printStackTrace();  
       }  
     }  
 }  


其执行结果是:

从结果看,取数据的时候并不能保证结果数据按照其原始顺序提取!我们对比一下上面这两段代码,发现貌似只是按顺序提取数据的那个代码多了个ExecutorCompletionService,其他部分基本相同,这样我们可以解释ExecutorCompletionService它的作用了,那就是
异步的执行任务,并按原始任务的顺序取得结果! 从ExecutorCompletionService源代码来看:

其构造函数中

 public ExecutorCompletionService(Executor executor) {
        if (executor == null)
            throw new NullPointerException();
        this.executor = executor;
        this.aes = (executor instanceof AbstractExecutorService) ?
            (AbstractExecutorService) executor : null;
        this.completionQueue = new LinkedBlockingQueue<Future<V>>();
    }

  实例化了一个堵塞队列,那么用这个干什么用哪?接下来看看submit的实现吧:

  

 public Future<V> submit(Callable<V> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<V> f = newTaskFor(task);
        executor.execute(new QueueingFuture(f));
        return f;
    }

    public Future<V> submit(Runnable task, V result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<V> f = newTaskFor(task, result);
        executor.execute(new QueueingFuture(f));
        return f;
    }

   呵呵,上面的submit实现解释Runnable和Callable是如何转化成Future的,另外还使用了Executor.execute(new QueueingFuture(f))! 而QueueingFuture是ExecutorCompletionService的一个私有内部类,我们看看它的

实现:

  

 private class QueueingFuture extends FutureTask<Void> {

        QueueingFuture(RunnableFuture<V> task) {

            super(task, null);

            this.task = task;

        }

        //completionQueue是使用的外部类的属性,即ExecutorCompletionService中的属性

        protected void done() { completionQueue.add(task); }

        private final Future<V> task;

    }

   Executor.execute方法会自动调用done方法并将Future加入到堵塞队列中,知道了吧?就是在这里把外部的Runnable或者Callable转化过来的Future和堵塞队列建立的联系(加入队列)completionQueue的类型是LinkedBlockingQueue LinkedBlockingQueue表明它是一个Queue,这就意味着它的元素是按先进先出(FIFO)的次序进行存储的。以特定次序插入的元素会以相同的次序被取出--但根据插入保证,任何从空队列中取出元素的尝试都会堵塞调用线程直到该元素可被取出时为止。同样地,任何向一个已满队列中插入元素的尝试将会堵塞调用线程直到该队列的存储空间有空余时为止。

    对于take()方法的实现:

public Future<V> take() throws InterruptedException {
        return completionQueue.take();
    }

   可以明确看到take()时,是从堵塞队列中take(),这可以解释为什么ExecutorCompletionService可以保证结果安装原始顺序输出的了!对于堵塞队列,我将在后面的文章中讨论到.我们在了解其基本原理的基础上,才能很好的使用它,不是吗?
综上,如果您的应用对结果数据的顺序没有要求,你就可以使用FutureTask和ExecutorService! 如果有要求,最好使用CompletionService!

 

 

 

源码地址: https://pan.quark.cn/s/d1f41682e390 miyoubiAuto 米游社每日米游币自动化Python脚本(务必使用Python3) 8更新:更换cookie的获取地址 注意:禁止在B站、贴吧、或各大论坛大肆传播! 作者已退游,项目不维护了。 如果有能力的可以pr修复。 小引一波 推荐关注几个非常可爱有趣的女孩! 欢迎B站搜索: @嘉然今天吃什么 @向晚大魔王 @乃琳Queen @贝拉kira 第三方库 食用方法 下载源码 在Global.py中设置米游社Cookie 运行myb.py 本地第一次运行时会自动生产一个文件储存cookie,请勿删除 当前仅支持单个账号! 获取Cookie方法 浏览器无痕模式打开 http://user.mihoyo.com/ ,登录账号 按,打开,找到并点击 按刷新页面,按下图复制 Cookie: How to get mys cookie 当触发时,可尝试按关闭,然后再次刷新页面,最后复制 Cookie。 也可以使用另一种方法: 复制代码 浏览器无痕模式打开 http://user.mihoyo.com/ ,登录账号 按,打开,找到并点击 控制台粘贴代码并运行,获得类似的输出信息 部分即为所需复制的 Cookie,点击确定复制 部署方法--腾讯云函数版(推荐! ) 下载项目源码和压缩包 进入项目文件夹打开命令行执行以下命令 xxxxxxx为通过上面方式或取得米游社cookie 一定要用双引号包裹!! 例如: png 复制返回内容(包括括号) 例如: QQ截图20210505031552.png 登录腾讯云函数官网 选择函数服务-新建-自定义创建 函数名称随意-地区随意-运行环境Python3....
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值