首页 > 其他分享 >线程池中CompletionService的应用

线程池中CompletionService的应用

时间:2022-12-07 20:00:47浏览次数:55  
标签:CompletionService System 池中 util println 线程 randomTime import out


当使用ExecutorService启动了多个Callable后,每个Callable会产生一个Future,我们需要将多个Future存入一个线性表,用于之后处理数据。当然,还有更复杂的情况,有5个生产者线程,每个生产者线程都会创建任务,所有任务的Future都存放到同一个线性表中。另有一个消费者线程,从线性表中取出Future进行处理。

CompletionService正是为此而存在,它是一个更高级的ExecutorService,它本身自带一个线程安全的线性表,无需用户额外创建。它提供了2种方法从线性表中取出结果,poll()是非阻塞的,若目前无结果,返回一个null,线程继续运行不阻塞。take()是阻塞的,若当前无结果,则线程阻塞,直到产生一个结果,被取出返回,线程才继续运行。

public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newCachedThreadPool();
CompletionService<Integer> comp = new ExecutorCompletionService<>(executor);
for(int i = 0; i<5; i++) {
comp.submit(new Task());
}
executor.shutdown();
int count = 0, index = 1;
while(count<5) {
Future<Integer> f = comp.poll();
if(f == null) {
System.out.println(index + " 没发现有完成的任务");
}else {
System.out.println(index + "产生了一个随机数: " + f.get());
count++;
}
index++;
TimeUnit.MILLISECONDS.sleep(500);
}
}
}

class Task implements Callable<Integer> {

@Override
public Integer call() throws Exception {
Random rand = new Random();
TimeUnit.SECONDS.sleep(rand.nextInt(7));
return rand.nextInt();
}

}


实际运用小案例:

模拟页面渲染

package com.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
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;


/**
* CompletionService的运用:当Executor遇见BlockingQueue时使用。
* 使用场景:
* 如果向Executor提交了一个批处理任务,并且希望在他们完成后获得结果。
*
* CompletionService整合了Executor和BlockingQueue的功能,你可以将Callable任务提交给它执行,
* 然后使用类似队列中的take和poll方法,在结果完整可用时(只是等待任意一个future的返回值),获得这个结果。就像一个大包的Future。
*
* completionService.take()的说明是:检索并移除已完成的任务,如果没有任何一个任务完成的,则继续等待
*
* 从案例的结果可以看出,每当图片下载完毕后,就会执行渲染操作。
* take方法只是检索completionService中所有future,看是否有执行完的任务,并获得结果。
*
* @author hadoop
*
*/
public class CompletionThread {
static ExecutorService mExecutor = Executors.newFixedThreadPool(5);
static int totalTimeDownPhoto =0;

/**
* 模拟页面渲染场景
*/
static void renderPage(){
final List<String> info = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
info.add("图片" + i);
}
CompletionService<String> completionService = new ExecutorCompletionService<String>(mExecutor);

/**
* 开启多线程处理下载图片的任务
*/
for(final String str : info){
completionService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
//下载图片download(str)
int randomTime = new Random().nextInt(9) + 1;//限制耗时不会出现0s,不会大于10s
Thread.sleep(randomTime*1000);
System.out.println("下载" + str +"耗费了" + randomTime + "s");
computeTime(randomTime);
return str;
}
});
}



try {
System.out.println("处理文字渲染的逻辑");
int randomTime = new Random().nextInt(9) + 1;
Thread.sleep(1000*randomTime);
computeTime(randomTime);
System.out.println("处理文字渲染的逻辑耗费了" + randomTime + "s");

/**
* 如果渲染图片耗时也比较久,也可以使用多线程。这里只是模拟,没有使用多线程处理渲染图片的过程
*/
for (int i = 0; i < info.size(); i++) {
//take检索并移除已完成的任务,如果没有任何一个任务完成的,则继续等待
Future<String> f = completionService.take();
//处理渲染图片的逻辑
randomTime = new Random().nextInt(3) + 1;
Thread.sleep(1000*randomTime);
computeTime(randomTime);
System.out.println("渲染"+f.get() +"耗时"+randomTime+"s");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}

/**
* 只有执行了shutdown方法,执行isTerminated才有效。否则isTerminated一直为ture
*/
mExecutor.shutdown();
while(true){
if(mExecutor.isTerminated()){
System.out.println("所有任务都执行完了,关闭线程池");
break;
}
}
}

/**
* 统计下载图片所花费的总时间
* @param randomTime
*/
static void computeTime(int randomTime){
synchronized (mExecutor) {
totalTimeDownPhoto += randomTime;
}
}


public static void main(String[] args) {
long start = System.currentTimeMillis();
renderPage();
long end = System.currentTimeMillis();
System.out.println("渲染页面总耗时:"+(end - start));
System.out.println("下载每张图片,渲染每张图片以及渲染文字的合计耗时是:"+ totalTimeDownPhoto);

int saveTime = (int) (totalTimeDownPhoto - (end - start)/1000);
System.out.println("总节约时间:"+ saveTime+"s");

}


}


标签:CompletionService,System,池中,util,println,线程,randomTime,import,out
From: https://blog.51cto.com/u_15905482/5920038

相关文章

  • java面试(多线程)
    1. Callable、Future、FutureTash详解Callable与Future是在JAVA的后续版本中引入进来的,Callable类似于Runnable接口,实现Callable接口的类与实现Runnable的类都是可以被线程......
  • JUC6 中断机制与线程通信三种让线程等待和唤醒的方法:
    1.线程中断1.1什么是线程中断①.一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止,所以,Thread.stop、Thread.suspend、Thread.resume都已经被废......
  • JUC5 多线程锁(下)
    1.​​synchronize​​锁升级:无锁,偏向锁,轻量锁,重量锁(看病:社区医院->三甲医院)1.1 概述按照获得锁和释放锁的性能消耗,锁的分类:1.无锁状态2.偏向锁:不进行​​CAS​​,测......
  • JUC4 多线程锁(上)
    1.乐观锁和悲观锁①.悲观锁什么是悲观锁?认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改适合写操作多的场......
  • 线程饥饿死锁
    线程饥饿死锁:   在一个线程池中,如果一个任务依赖于其他任务的执行,就可能产生死锁。对应一个单线程话的Executor,一个任务将另一个任务提交到相同的Executor中,并等待......
  • 线程的取消和中断
    下面介绍取消线程常用的4中方式:一、通过设置“cancelled requested”标志来中断线程java中的任务取消实现:    是通过一个协作机制完成的,使用一个线程能够要求另......
  • 进程、线程、并行、并发、串行、进程池
    进程什么是进程?开发时编写的代码,我们称为程序。当我们运行一个程序,那么我们将运行的程序叫做进程。进程是线程的容器。程序与进程的区别程序是数据和指令的集合,是静......
  • 多线程--面试题整理
    简述线程,程序、进程的基本概念线程:与进程相似,但线程是比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空......
  • 线程协作之生产者消费者模式
    线程协作生产者消费者模式管程法packagecom.deng.gaoji;//测试生产者消费者模型--》利用缓冲区解决:管程法//生产者,消费者,产品,缓冲区publicclassTestPC{......
  • 程序、进程、线程
    一、概述程序:存放在磁盘中的可执行文件,对于计算机来说它就是一系列的指令集合,是一个静态概念进程: 程序的一次执行过程,是一个动态的概念,进程由进程控制块、程序段......