future.get方法阻塞问题的解决,实现按照任务完成的先后顺序获取任务的结果

1、Future

Future模式是多线程设计常用的一种设计模式。Future模式可以理解成:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。
Future提供了三种功能:
判断任务是否完成
能够中断任务
能够获取任务执行的结果
向线程池中提交任务的submit方法不是阻塞方法,而Future.get方法是一个阻塞方法,当submit提交多个任务时,只有所有任务都完成后,才能使用get按照任务的提交顺序得到返回结果,所以一般需要使用future.isDone先判断任务是否全部执行完成,完成后再使用future.get得到结果。(也可以用get (long timeout, TimeUnit unit)方法可以设置超时时间,防止无限时间的等待)
三段式的编程:1.启动多线程任务2.处理其他事3.收集多线程任务结果,Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,要么使用阻塞,在future.get()的地方等待future返回的结果,这时又变成同步操作;要么使用isDone()轮询地判断Future是否完成,这样会耗费CPU的资源。
解决方法:CompletionService和CompletableFuture(按照任务完成的先后顺序获取任务的结果

2、CompletionService是java1.8之前最好用的方法,

能够实现按照任务完成的先后顺序获取任务的结果。

public class TestCompletionService {
	private static final String commandstr01 = "hahah";
	private static final String commandstr02 = "hahah";
	
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//1、创建一个线程池
		ExecutorService executorService = Executors.newCachedThreadPool();
		
		CompletionService<String> completionService = new ExecutorCompletionService<String>(executorService);
		
		completionService.submit(new MyThreadt33(commandstr01));
		completionService.submit(new MyThreadt44(commandstr01));
			
		executorService.shutdown();
		
		System.out.println(completionService.take().get());
		System.out.println(completionService.take().get());
	}
}

class MyThreadt33 implements Callable<String>{
	private String commandstr;          // 要运行的mingling
	public MyThreadt33(String commandstr) {
		this.commandstr = commandstr;
	}
	@Override
	public String call() throws Exception {
		int sum = 0;
		for (int i = 0; i < 100; i++) {
			Thread.sleep(200);
			sum += i;
			System.out.println("Mythread3: "+i);
		}
		return String.valueOf(sum+300000);
	}
}

class MyThreadt44 implements Callable<String>{
	private String commandstr;          // 要运行的mingling
	public MyThreadt44(String commandstr) {
		this.commandstr = commandstr;
	}
	@Override
	public String call() throws Exception {
		int sum = 0;
		for (int i = 0; i < 50; i++) {
			Thread.sleep(200);
			sum += i;
			System.out.println("Mythread4: "+i);
		}
		return String.valueOf(sum+400000);
	}
}

CompletionService方法可以通过completionService.take().get()方法获取最快完成的线程的返回结果(若当前没有线程执行完成则阻塞直到最快的线程执行结束),第二次调用则返回第二快完成的线程的返回结果。

3、CompletableFuture接口

所谓异步调用其实就是实现一个可无需等待被调函数的返回值而让操作继续运行的方法。简单的讲就是另启一个线程来完成调用中的部分计算,使调用继续运行或返回,而不需要等待计算结果。但调用者仍需要取线程的计算结果。

JDK1.5新增了Future接口,用于描述一个异步计算的结果。虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。

JDK1.8后提出了CompletableFuture接口实现了Future和CompletionStage两个接口,CompletionStage可以看做是一个异步任务执行过程的抽象(CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段,一个阶段的计算执行可以是一个Function,Consumer或者Runnable。比如:

stage.thenApply(x -> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() -> System.out.println()))

我们可以基于CompletableFuture创建任务和链式处理多个任务,并实现按照任务完成的先后顺序获取任务的结果。

(1)创建任务

##使用runAsync方法新建一个线程来运行Runnable对象(无返回值)

##使用supplyAysnc方法新建线程来运行Supplier<T>对象(有返回值)

##基于线程池创建

(2)任务的异步处理

不论Future.get()方法还是CompletableFuture.get()方法都是阻塞的,为了获取任务的结果同时不阻塞当前线程的执行,我们可以使用CompletionStage提供的方法结合callback来实现任务的异步处理。

##whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。
##whenCompleteAsync:把 whenCompleteAsync 这个任务继续提交给线程池来进行执行,也就是并行执行。

##thenApply:当一个线程依赖另一个线程时,可以使用 thenApply 方法来把这两个线程串行化

##thenAcceptthenAccept接收上一阶段的输出作为本阶段的输入,并消费处理,无返回结果。 

##thenRun:不关心前一阶段的计算结果,因为它不需要输入参数,进行消费处理,无返回结果。

## thenCombine:会把两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给 thenCombine 来处理。

## applyToEither :两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的转化操作。

##acceptEither 方法:两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的消耗操作

public class TestCompletableFuture {
	private static final String commandstr01 = "hahah";
	private static final String commandstr02 = "hahah";
	private static final String commandstr03 = "hahah";
	private static final String commandstr04 = "hahah";

	    public static void main(String[] args) throws InterruptedException, ExecutionException{
	        
	    	ExecutorService executorService = Executors.newCachedThreadPool();
	    	
	        CompletableFuture.supplyAsync(new MyThreadt444(commandstr02),executorService).whenComplete((result, e) -> {
	        	//执行线程执行完以后的操作。
	            System.out.println(result + " " + e);
	        }).exceptionally((e) -> {
	            //抛出异常
	        	System.out.println("exception " + e);
	            return "exception";
	        });
	        
	         CompletableFuture.supplyAsync(new MyThreadt333(commandstr02),executorService).whenComplete((result, e) -> {
	        	//执行线程执行完以后的操作。
	        	System.out.println(result + " " + e);
	        }).exceptionally((e) -> {
	            System.out.println("exception " + e);
	            return "exception";
	        });
	    }
}



class MyThreadt333 implements Supplier<String>{

	private String commandstr;          // 要运行的mingling
	public MyThreadt333(String commandstr) {
		this.commandstr = commandstr;
	}
	@Override
	public String get() {
		int sum = 0;
		for (int i = 0; i < 30; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			sum += i;
			System.out.println("Mythread333: "+i);
		}
		return String.valueOf(sum+300000);
	}
}

class MyThreadt444 implements Supplier<String>{

	private String commandstr;          // 要运行的mingling
	public MyThreadt444(String commandstr) {
		this.commandstr = commandstr;
	}
	@Override
	public String get() {
		int sum = 0;
		for (int i = 0; i < 40; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			sum += i;
			System.out.println("Mythread444: "+i);
		}
		return String.valueOf(sum+400000);
	}
}

在CompletableFuture接口中除了使用whenComplete还可以使用handle等方法能实现按照任务完成的先后顺序获取任务的结果。

4、几种多线程并发取结果方式的总结

下图参考自:https://blog.csdn.net/u011726984/article/details/79320004

<think>好的,我需要帮助用户寻找Java中实现异步导出任务的公共方法。首先,我需要理解用户的需求。用户想要一个通用的工具方法,用于异步导出任务,可能在后台处理数据生成,避免阻塞主线程。常见的实现方式可能涉及线程池、异步框架或者Spring的@Async注解。 接下来,我要回忆Java中处理异步任务的常用方法。线程池是一个基础的方式,可以使用ExecutorService来提交任务。另外,如果用户的项目使用了Spring框架,那么@Async注解会是一个更简洁的选择。CompletableFuture也是Java 8之后推荐的异步编程方式,可以结合使用。 然后,我需要考虑如何将这些方法封装成公共的实用工具类。可能需要设计一个ExportUtils类,里面包含静态方法,接收导出任务的参数,比如数据生成逻辑、回调处理等。同时,需要处理异常情况,确保任务失败时有适当的日志记录或回调通知。 用户提供的引用中有提到异步操作不等待测试的情况,这可能涉及到任务提交后立即返回,但需要确保任务被正确调度和执行。例如,引用[1]中提到在测试中不等待异步操作,但需要验证按钮状态,类似地,导出任务提交后可能需要返回任务ID或状态,让调用方可以轮询或接收回调。 另外,引用[3]提到使用模拟数据,可能导出任务需要生成大量数据,异步处理可以避免前端长时间等待。引用[5]中的Node.js示例使用require引入模块,可能在Java中需要类似的工具类结构,确保方法可重用。 然后,我需要确保代码示例的正确性。比如,使用Spring的@Async需要配置异步支持,线程池的参数需要合理设置,避免资源耗尽。同时,处理CompletableFuture时要注意异常处理和链式调用。 可能还需要考虑任务的状态跟踪,比如返回一个Future对象,让调用方可以查询任务是否完成,或者获取结果。此外,日志记录和错误处理也是关键,确保导出任务中的异常能被捕获并处理,而不是默默失败。 最后,生成相关问题需要考虑用户可能遇到的扩展需求,比如如何监控任务进度、如何处理大数据量导出、如何保证任务顺序等。这些问题可以帮助用户进一步优化他们的异步导出实现。</think>### Java异步导出任务实现方法 在Java中实现异步导出任务,可通过线程池或Spring框架的异步支持实现。以下是两种常用方法及工具类封装: --- #### 方法一:使用线程池工具类(原生Java) ```java import java.util.concurrent.*; public class ExportUtils { private static final ExecutorService executor = Executors.newFixedThreadPool(4); // 根据需求调整线程数 /** * 提交异步导出任务 * @param task 导出任务逻辑(实现Callable接口) * @return Future对象用于获取任务状态/结果 */ public static <T> Future<T> asyncExport(Callable<T> task) { return executor.submit(task); } } // 使用示例 Future<File> exportFuture = ExportUtils.asyncExport(() -> { // 生成Excel/CSV等导出文件 return generateReport(); }); ``` --- #### 方法二:使用Spring框架的@Async注解 ```java import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.stereotype.Component; @EnableAsync @Component public class ExportAsyncService { /** * 异步导出方法 * @param params 导出参数 */ @Async("taskExecutor") // 指定自定义线程池 public void asyncExportData(Map<String, Object> params) { try { // 执行数据查询与文件生成 generateExportFile(params); } catch (Exception e) { // 异常处理逻辑 log.error("导出失败", e); } } } // 配置线程池(application.yml) spring: task: execution: pool: core-size: 5 max-size: 10 ``` --- #### 方法三:CompletableFuture实现(Java 8+) ```java public class ExportUtility { public static CompletableFuture<Void> asyncExport(Runnable task) { return CompletableFuture.runAsync(task, ForkJoinPool.commonPool()); // 可替换为自定义线程池 } } // 使用示例 ExportUtility.asyncExport(() -> { // 导出逻辑 exportService.processData(); }).thenRun(() -> System.out.println("导出完成")); ``` --- ### 关键设计要点 1. **状态跟踪**:返回Future/CompletableFuture对象用于查询任务状态 2. **异常处理**:通过`Future.get()`捕获异常或使用异常回调 3. **资源隔离**:建议为IO密集型任务使用单独线程池[^1] 4. **进度通知**:可通过回调接口或事件机制实现进度更新 ---
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值