java异步编程

本文介绍了Java中实现异步编程的几种方式,包括jdk1.8之前的Future、Guava的ListenableFuture及Netty的Promise,并展示了如何使用CompletableFuture结合Lambda表达式简化代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        很多时候我们都希望能够最大的利用资源,比如在进行IO操作的时候尽可能的避免同步阻塞的等待,因为这会浪费CPU的资源。如果在有可读的数据的时候能够通知程序执行读操作甚至由操作系统内核帮助我们完成数据的拷贝,这再好不过了。从NIO到CompletableFuture、LambdaFork/Join,java一直在努力让程序尽可能变的异步甚至拥有更高的并行度,这一点一些函数式语言做的比较好,因此java也或多或少的借鉴了某些特性。下面介绍一种非常常用的实现异步操作的方式。

考虑有一个耗时的操作,操作完后会返回一个结果(不管是正常结果还是异常),程序如果想拥有比较好的性能不可能由线程去等待操作的完成,而是应该采用listener模式。jdk并发包里的Future代表了未来的某个结果,当我们向线程池中提交任务的时候会返回该对象。代码例子:

/**
 * jdk1.8之前的Future
 * 
 * @author Administrator
 *
 */
public class JavaFuture {
	public static void main(String[] args) throws Throwable, ExecutionException {
		ExecutorService executor = Executors.newFixedThreadPool(1);
		// Future代表了线程执行完以后的结果,可以通过future获得执行的结果
		// 但是jdk1.8之前的Future有点鸡肋,并不能实现真正的异步,需要阻塞的获取结果,或者不断的轮询
		// 通常我们希望当线程执行完一些耗时的任务后,能够自动的通知我们结果,很遗憾这在原生jdk1.8之前
		// 是不支持的,但是我们可以通过第三方的库实现真正的异步回调
		Future<String> f = executor.submit(new Callable<String>() {

			@Override
			public String call() throws Exception {
				System.out.println("task started!");
				Thread.sleep(3000);
				System.out.println("task finished!");
				return "hello";
			}
		});

		//此处阻塞main线程
		System.out.println(f.get());
		System.out.println("main thread is blocked");
	}
}
如果想获得耗时操作的结果,可以通过get方法获取,但是该方法会阻塞当前线程,我们可以在做完剩下的某些工作的时候调用get方法试图去获取结果,也可以调用非阻塞的方法isDone来确定操作是否完成,这种方式有点儿类似下面的过程:


这种方式对流程的控制很混乱,但是在jdk1.8之前只提供了这种笨拙的实现方式,以至于很多高性能的框架都实现了自己的一套异步框架,比如Netty和Guava,下面分别介绍下这三种异步的实现方式(包括jdk1.8)。首先是Guava中的实现方式:

package guava;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

/**
 * Guava中的Future
 * 
 * @author Administrator
 *
 */
public class GuavaFuture {
	public static void main(String[] args) {
		ExecutorService executor = Executors.newFixedThreadPool(1);

		// 使用guava提供的MoreExecutors工具类包装原始的线程池
		ListeningExecutorService listeningExecutor = MoreExecutors.listeningDecorator(executor);
		//向线程池中提交一个任务后,将会返回一个可监听的Future,该Future由Guava框架提供
		ListenableFuture<String> lf = listeningExecutor.submit(new Callable<String>() {

			@Override
			public String call() throws Exception {
				System.out.println("task started!");
				//模拟耗时操作
				Thread.sleep(3000);
				System.out.println("task finished!");
				return "hello";
			}
		});
		//添加回调,回调由executor中的线程触发,但也可以指定一个新的线程
		Futures.addCallback(lf, new FutureCallback<String>() {

			//耗时任务执行失败后回调该方法
			@Override
			public void onFailure(Throwable t) {
				System.out.println("failure");
			}
			
			//耗时任务执行成功后回调该方法
			@Override
			public void onSuccess(String s) {
				System.out.println("success " + s);
			}
		});
		
		//主线程可以继续做其他的工作
		System.out.println("main thread is running");
	}
}
Guava提供了一套完整的异步框架,核心是可监听的Future,通过注册监听器或者回调方法实现及时获取操作结果的能力。需要提一点的是,假设添加监听的时候耗时操作已经执行完了,此时回调方法会被立即执行并不会丢失。想探究其实现方式的话可以跟一下源码,底层的原理并不难。

谈到异步编程就不得不提一下Promise,很多函数式语言比如js原生支持Promise,但是在java界也有一些promise框架,其中就有大名鼎鼎的Netty。从Future、Callback到Promise甚至线程池,Netty实现了一套完整的异步框架,并且netty代码中也大量使用了Promise,下面是Netty中的例子:

package netty_promise;

import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;

/**
 * netty中的promise
 * 
 * @author Administrator
 *
 */
public class PromiseTest {
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static void main(String[] args) throws Throwable {
		//线程池
		EventExecutorGroup group = new DefaultEventExecutorGroup(1);
		//向线程池中提交任务,并返回Future,该Future是netty自己实现的future
		//位于io.netty.util.concurrent包下,此处运行时的类型为PromiseTask
		Future<?> f = group.submit(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("任务正在执行");
				//模拟耗时操作,比如IO操作
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("任务执行完毕");
			}
		});
		//增加监听
		f.addListener( new FutureListener() {
			@Override
			public void operationComplete(Future arg0) throws Exception {
				System.out.println("ok!!!");
			}
		});
		System.out.println("main thread is running.");
	}
}
直到jdk1.8才算真正支持了异步操作,其中借鉴了某些框架的实现思想,但又有新的功能,同时在jdk1.8中提供了lambda表达式,使得java向函数式语言又靠近了一步。借助jdk原生的CompletableFuture可以实现异步的操作,同时结合lambada表达式大大简化了代码量。代码例子如下:

package netty_promise;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;

/**
 * 基于jdk1.8实现任务异步处理
 * 
 * @author Administrator
 *
 */
public class JavaPromise {
	public static void main(String[] args) throws Throwable, ExecutionException {
		// 两个线程的线程池
		ExecutorService executor = Executors.newFixedThreadPool(2);
		//jdk1.8之前的实现方式
		CompletableFuture<String> future = CompletableFuture.supplyAsync(new Supplier<String>() {
			@Override
			public String get() {
				System.out.println("task started!");
				try {
					//模拟耗时操作
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				return "task finished!";
			}
		}, executor);

		//采用lambada的实现方式
		future.thenAccept(e -> System.out.println(e + " ok"));
		
		System.out.println("main thread is running");
	}
}
以上的三种实现方式类似下面的过程:


上面的图只是简单的表示了一下异步的实现流程,实际的调用中看似顺序的步骤会发生线程的切换。

<think>好的,用户想了解Java中的异步编程实现方式和示例。首先,我需要回顾一下Java中常用的异步编程方法,确保覆盖主要的几种方式。根据提供的引用内容,用户提到了多线程、CompletableFuture、响应式编程(如Reactor),可能还有其他的比如回调、Future接口等。 首先,用户可能希望得到一个结构清晰的回答,分点列出不同的实现方式,每种方式都有示例代码和说明。需要确保每个示例都符合语法规范,并且解释优缺点,帮助用户理解适用场景。 接下来,我需要检查引用中的内容。引用[1]展示了多线程的基本用法,引用[2]详细介绍了CompletableFuture,引用[3]提到了Reactor框架。这些应该分别作为不同的实现方式来介绍。 要注意示例代码的正确性,比如在CompletableFuture部分,需要展示如何创建异步任务和组合结果。同时,响应式编程部分可能需要说明回压机制和适用场景,但用户示例中的代码使用了Mono,所以应该保持简洁。 此外,用户可能对每种方式的优缺点感兴趣,所以在每个部分后需要简要说明。比如多线程简单但难以管理,CompletableFuture更灵活但复杂,响应式编程适合数据流但学习曲线高。 还要考虑是否遗漏了其他常见的异步方法,比如使用ExecutorService的Future对象,或者第三方库如Guava的ListenableFuture。但根据提供的引用,可能主要集中在这三种方式。 最后,需要生成相关问题,可能涉及性能比较、异常处理、选择哪种方式等,帮助用户进一步学习。需要确保问题相关且具有引导性。 现在需要组织内容,确保每个部分有清晰的标题、代码示例、说明和优缺点。同时,引用正确的来源,比如引用[1]、[2]、[3]对应的示例。注意代码格式使用正确的语法,比如Java代码块,并转义特殊符号如尖括号。 可能需要注意用户提供的引用中的代码是否有错误,比如ReactorExample中的Mono使用是否正确,但根据示例代码,应该是正确的。确保转义字符正确,比如在代码中使用<和>的地方,可能需要调整成正确的符号。 总结,回答应分为多线程、CompletableFuture、响应式编程三个主要部分,每个部分包含示例代码、说明、优缺点,并引用对应的引用来源。最后生成相关问题,如不同方法的对比、异常处理、应用场景等。</think>### Java异步编程实现方式及示例 Java异步编程的核心目标是提升程序的并发性能和资源利用率。以下是三种主流的实现方式及示例: --- #### 一、基础多线程实现 通过`Thread`类直接创建线程实现异步,适合简单场景。 **示例代码**: ```java public class AsyncExample { public static void main(String[] args) { Thread task = new Thread(() -> { System.out.println("异步任务开始执行"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("异步任务执行完毕"); }); task.start(); System.out.println("主线程继续执行"); } } ``` **说明**: - 优点:实现简单,无需依赖外部库[^1]。 - 缺点:线程管理复杂,资源消耗高。 --- #### 二、CompletableFutureJava 8+) `CompletableFuture`提供链式调用和任务组合能力,适合复杂异步流程。 **示例代码**: ```java import java.util.concurrent.CompletableFuture; public class CompletableFutureExample { public static void main(String[] args) { CompletableFuture.supplyAsync(() -> { System.out.println("异步任务启动"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "任务结果"; }).thenAccept(result -> { System.out.println("处理结果: " + result); }); System.out.println("主线程继续运行"); } } ``` **说明**: - 优点:支持任务组合(如`thenApply`、`thenCombine`),内置异常处理[^2]。 - 缺点:代码复杂度较高,需理解函数式编程。 --- #### 三、响应式编程(Project Reactor) 通过`Mono`和`Flux`处理异步数据流,适合高并发和背压场景。 **示例代码**: ```java import reactor.core.publisher.Mono; public class ReactorExample { public static void main(String[] args) { Mono.fromSupplier(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "Hello, Reactor!"; }).subscribe(result -> { System.out.println(result); }); System.out.println("主线程继续运行"); } } ``` **说明**: - 优点:支持背压(Backpressure),适合流式数据处理[^3]。 - 缺点:学习曲线陡峭,需掌握响应式编程范式。 --- #### 四、其他方式(补充) 1. **Future + ExecutorService**: 通过`ExecutorService`提交任务并返回`Future`对象,支持异步结果获取。 2. **回调机制**: 结合接口或Lambda实现任务完成后的回调逻辑。 --- ### 对比与选择建议 | 方式 | 适用场景 | 复杂度 | |------------------|----------------------------|-------| | 基础多线程 | 简单异步任务 | 低 | | CompletableFuture| 复杂任务链、结果依赖 | 中 | | 响应式编程 | 高并发数据流、背压控制 | 高 | ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值