异步获取多线程返回的数据

该问题涉及到四个接口:Callable、Runnable、Future、CompletionService,还有一个类:FutureTask。下面分别进行下简单介绍:

一、Future:

在这里插入图片描述
V get() :获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成。

V get(Long timeout , TimeUnit unit) :获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,如果阻塞时间超过设定的timeout时间,该方法将抛出异常。

boolean isDone() :判断任务是否完成如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true。

boolean isCanceller() :如果任务完成前被取消,则返回true。

boolean cancel(boolean mayInterruptRunning) :如果任务还没开始,执行cancel(…)方法将返回false;如果任务已经启动,执行cancel(true)方法将以中断执行此任务线程的方式来试图停止任务,如果停止成功,返回true;当任务已经启动,执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时返回false;当任务已经完成,执行cancel(…)方法将返回false。mayInterruptRunning参数表示是否中断执行中的线程。

通过方法分析我们也知道实际上Future提供了3种功能:

(1)能够中断执行中的任务

(2)判断任务是否执行完成

(3)获取任务执行完成后的结果。

二、ExecutorService的submit方法

在这里插入图片描述
Callable接口的call方法有返回值,Runnable接口的run方法没有返回值。

submit(Runnable):Future<?> ,返回的Future无法获取返回值。submit(Runnable, T):Future,返回的Future也无法获取返回值。

三、FutureTask

FutureTask定义

public class FutureTask<V> implements RunnableFuture<V> {
 ...
     public V get() throws InterruptedException, ExecutionException {
         int s = state;
         if (s <= COMPLETING)
             s = awaitDone(false, 0L);
         return report(s);
    }
    ...
 }

RunnableFuture定义

 public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
      * Sets this Future to the result of its computation
      * unless it has been cancelled.
      */
    void run();
 }

同时实现了Runnable、Future,所以,FutureTask

ExecutorService的submit方法接收Runnable和Callable,所以也可以接收FutureTask。

我们知道Thread只支持Runnable,所以也支持FutureTask。FutureTask让Thread也可以有返回值的效果。

四、实现异步获取多线程返回数据的三种方式:

  1. 通过Callable+Future,Callable负责执行返回,Future负责接收。Callable接口对象可以交给ExecutorService的submit方法去执行。
	/**
      * Callable接口配合ExecutorService的submit方法
      */
     public static void asynTask1() {
         try {
             //使用Callable
             Callable call = new Callable() {
                 @Override
                 public String call() throws Exception {
                     Thread.sleep(100l);
                     return "asynTask1 result";
                }
            };
             //使用ExecutorService的submit
             ExecutorService executorService = Executors.newSingleThreadExecutor();
             Future<String> future = executorService.submit(call);
 
             //获取结果,get是阻塞方法
             String result = future.get();
             System.out.println("结果:" + result);
        } catch (InterruptedException e) {
             e.printStackTrace();
        } catch (ExecutionException e) {
             e.printStackTrace();
        }
    }

结果:
在这里插入图片描述
2. 通过Callable+FutureTask,Callable负责执行返回,FutureTask负责接收。FutureTask同时实现了Runnable和Callable接口,可以给到ExecutorService的submit方法和Thread去执行。

Callable接口配合FutureTask,ExecutorService的submit方法去执行

 public class AsynTask2 {
     public static void main(String[] args) {
         asynTask2();
         System.out.println("主线程执行完毕");
    }
 
     /**
      * Callable接口配合FutureTask,ExecutorService的submit方法去执行
      */
     private static void asynTask2() {
         try {
             //使用FutureTask
             FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
                 public String call() throws Exception {
                     Thread.sleep(100l);
                     //返回结果
                     return "asynTask2 result";
                }
            });
             //使用ExecutorService
             ExecutorService executorService = Executors.newSingleThreadExecutor();
             executorService.submit(futureTask);
 
             //获取结果,get是阻塞方法
             String result = futureTask.get();
             System.out.println("结果:" + result);
        } catch (InterruptedException e) {
             e.printStackTrace();
        } catch (ExecutionException e) {
             e.printStackTrace();
        }
    }
 
 }

结果:
在这里插入图片描述
Callable接口配合FutureTask,给Thread类去执行

public class AsynTask3 {
    public static void main(String[] args) {
        asynTask3();
        System.out.println("主线程执行完毕");
    }
 
    /**
      * Callable接口配合FutureTask,给Thread类去执行
      */
    private static void asynTask3() {
        try {
            FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
                public String call() throws Exception {
                    Thread.sleep(100l);
                    //返回结果
                    return "asynTask3 result";
                }
            });
            //使用Thread
            Thread thread = new Thread(futureTask);
            thread.start();
            //获取结果,get是阻塞方法
            String result = futureTask.get();
            System.out.println("结果:" + result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
 }

结果:
在这里插入图片描述

  1. 通过CompletionService,jdk1.8之后提供了完成服务CompletionService,可以实现这样的需求。

CompletionService配合Callable

public class AsynTask4 {
     public static void main(String[] args) {
         asynTask4();
         System.out.println("主线程执行完毕");
    }
 
     /**
      * CompletionService配合Callable
      */
     private static void asynTask4() {
         try {
             //使用Callable
             Callable call = new Callable() {
                 @Override
                 public String call() throws Exception {
                     Thread.sleep(100l);
                     return "asynTask4 result";
                }
            };
 
             //使用ExecutorService
             ExecutorService executorService = Executors.newSingleThreadExecutor();
 
             // 构建完成服务
             CompletionService<String> completionService = new ExecutorCompletionService<String>(executorService);
             completionService.submit(call);
 
             //获取结果,get是阻塞方法
             String result = completionService.take().get();
             System.out.println("结果:" + result);
        } catch (InterruptedException e) {
             e.printStackTrace();
        } catch (ExecutionException e) {
             e.printStackTrace();
        }
    }
 }

结果:
在这里插入图片描述

注意,实现Runnable接口任务执行结束后无法获取执行结果,(Callable有返回值,Runnable没有返回值)。
在这里插入图片描述
CompletionService和Future的区别是什么?

  • Future获取结果,一个一个的取,一个取完了,在取另外一个,就会等待
  • CompletionService,任意一个线程有返回,就立马取出
public class AsynTask5 {
     public static void main(String[] args) {
         asynCompletionService();
         System.out.println("----------------分割线-------------------");
         asynFuture();
         System.out.println("主线程执行完毕");
    }
 
     private static void asynCompletionService() {
         try {
             //使用ExecutorService
             ExecutorService executorService = Executors.newCachedThreadPool();
 
             // 构建完成服务
             CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(executorService);
             for (int i = 1; i <= 5; i++) {
                 //提交任务
                 completionService.submit(new HandleFuture<>(i));
            }
             //获取结果,一个一个阻塞的取出。这中间肯定会浪费一定的时间在等待上
             for (int i = 1; i <= 5; i++) {
                 Integer result = completionService.take().get();
                 System.out.println("结果:" + result);
            }
        } catch (InterruptedException e) {
             e.printStackTrace();
        } catch (ExecutionException e) {
             e.printStackTrace();
        }
    }
 
     private static void asynFuture() {
         try {
             //使用ExecutorService
             ExecutorService executorService = Executors.newCachedThreadPool();
 
             //Future列表
             List<Future<Integer>> result = new ArrayList<Future<Integer>>();
             for (int i = 1; i <= 5; i++) {
                 //提交任务
                 Future<Integer> submit = executorService.submit(new HandleFuture<>(i));
                 result.add(submit);
            }
             //获取结果,输出和线程的放入顺序无关系。每一个线程执行成功后,立刻就输出
             for (Future<Integer> integerFuture : result) {
                 Integer integer = integerFuture.get();
                 System.out.println("结果:" + integer);
            }
        } catch (InterruptedException e) {
             e.printStackTrace();
        } catch (ExecutionException e) {
             e.printStackTrace();
        }
    }
 }
 
 class HandleFuture<Integer> implements Callable<Integer> {
     private Integer num;
 
     public HandleFuture(Integer num) {
         this.num = num;
    }
 
     @Override
     public Integer call() throws Exception {
         Thread.sleep(2 * 1000l);
         System.out.println(Thread.currentThread().getName());
         return num;
    }
 
 }

执行结果;
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
清理数据是一个比较耗时的操作,如果使用单线程处理会影响系统的性能。因此,在处理大量数据时,我们可以考虑使用多线程进行处理。下面是使用Spring Boot多线程批量分页清理数据的流程: 1. 首先,我们需要定义一个数据清理的服务类。在这个类中,我们可以使用@Async注解来标记需要异步执行的方法。 2. 在清理数据方法中,我们可以使用分页查询来获取需要清理的数据。使用分页可以避免一次性查询大量数据的情况,从而提高查询效率。查询出来的数据可以使用List集合存储。 3. 接下来,我们可以使用Java中的Executor框架来创建线程池。线程池中包含多个线程,可以同时执行多个任务。 4. 将清理数据的任务分配给线程池中的线程进行处理。每个线程处理一部分数据,处理完成后将结果返回。 5. 在所有线程处理完成后,我们可以将结果合并起来,得到最终的清理结果。 6. 最后,我们需要关闭线程池,释放资源。 下面是一个示例代码: ```java @Service public class DataCleanService { @Autowired private DataRepository dataRepository; @Async public CompletableFuture<Integer> cleanData(int pageSize) { int total = 0; int offset = 0; while (true) { // 分页查询需要清理的数据 List<Data> dataList = dataRepository.findByCondition(pageSize, offset); if (dataList.isEmpty()) { break; } // 使用线程池清理数据 Executor executor = Executors.newFixedThreadPool(4); List<CompletableFuture<Integer>> futures = new ArrayList<>(); for (Data data : dataList) { futures.add(CompletableFuture.supplyAsync(() -> { // 清理单条数据 int result = dataRepository.cleanData(data); return result; }, executor)); } // 等待所有线程执行完成 CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])); allFutures.join(); // 统计清理结果 int count = futures.stream().map(CompletableFuture::join).reduce(0, Integer::sum); total += count; offset += pageSize; } return CompletableFuture.completedFuture(total); } } ``` 在上面的代码中,我们定义了一个cleanData方法来清理数据。我们使用了@Async注解来标记这个方法需要异步执行。在方法中,我们使用分页查询来获取需要清理的数据,并将数据分配给线程池中的线程进行处理。在处理完成后,我们统计每个线程的清理结果,并将结果合并起来。最后,我们将清理结果封装成CompletableFuture对象返回。 需要注意的是,在使用多线程进行数据清理时,我们需要注意线程安全。在对同一份数据进行处理时,需要保证线程安全,避免出现数据冲突的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值