Java|Java 异步实现的几种方式小结

Java 异步实现的几种方式 1. jdk1.8之前的Future
jdk并发包里的Future代表了未来的某个结果,当我们向线程池中提交任务的时候会返回该对象,可以通过future获得执行的结果,但是jdk1.8之前的Future有点鸡肋,并不能实现真正的异步,需要阻塞的获取结果,或者不断的轮询。
通常我们希望当线程执行完一些耗时的任务后,能够自动的通知我们结果,很遗憾这在原生jdk1.8之前是不支持的,但是我们可以通过第三方的库实现真正的异步回调。

/** * jdk1.8之前的Future * @author Administrator */public class JavaFuture { public static void main(String[] args) throws Throwable, ExecutionException {ExecutorService executor = Executors.newFixedThreadPool(1); Future f = executor.submit(new Callable() { @Overridepublic String call() throws Exception {System.out.println("task started!"); longTimeMethod(); System.out.println("task finished!"); return "hello"; }}); //此处get()方法阻塞main线程System.out.println(f.get()); System.out.println("main thread is blocked"); }}

如果想获得耗时操作的结果,可以通过get()方法获取,但是该方法会阻塞当前线程,我们可以在做完剩下的某些工作的时候调用get()方法试图去获取结果。
也可以调用非阻塞的方法isDone来确定操作是否完成,isDone这种方式有点儿类似下面的过程:
Java|Java 异步实现的几种方式小结
文章图片

2. jdk1.8开始的Future
直到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 future = CompletableFuture.supplyAsync(new Supplier() {@Overridepublic String get() {System.out.println("task started!"); try {//模拟耗时操作longTimeMethod(); } 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"); }}

实现方式类似下图:
Java|Java 异步实现的几种方式小结
文章图片

3. Spring的异步方法
先把longTimeMethod 封装到Spring的异步方法中,这个异步方法的返回值是Future的实例。这个方法一定要写在Spring管理的类中,注意注解@Async。
@Servicepublic class AsynchronousService{@Asyncpublic Future springAsynchronousMethod(){Integer result = longTimeMethod(); return new AsyncResult(result); }}

其他类调用这个方法。这里注意,一定要其他的类,如果在同类中调用,是不生效的。
@Autowiredprivate AsynchronousService asynchronousService; public void useAsynchronousMethod(){Future future = asynchronousService.springAsynchronousMethod(); future.get(1000, TimeUnit.MILLISECONDS); }

其实Spring只不过在原生的Future中进行了一次封装,我们最终获得的还是Future实例。
4. Java如何将异步调用转为同步
换句话说,就是需要在异步调用过程中,持续阻塞至获得调用结果。
  • 使用wait和notify方法
  • 使用条件锁
  • Future
  • 使用CountDownLatch
  • 使用CyclicBarrier
五种方法,具体举例说明这篇文章
java异步任务处理 1、场景
最近做项目的时候遇到了一个小问题:从前台提交到服务端A,A调用服务端B处理超时,原因是前端一次请求往db插1万数据,插完之后会去清理缓存、发送消息。
服务端的有三个操作 a、插DB b、清理cache c、发送消息。1万条数据,说多不多,说少不少.况且不是单单insert。出现超时现象,不足为奇了吧~~
2、分析
如何避免超时呢?一次请求处理辣么多数据,可分多次请求处理,每次请求处理100条数据,可以有效解决超时问题. 为了不影响用户的体验,请求改为ajax 异步请求。
除此之外,仔细分析下场景. a 操作是本次处理的核心. 而b、c操作可以异步处理。换句话说,a操作处理完可以马上返回给结果, 不必等b、c处理完再返回。b、c操作可以放在一个异步任务去处理。
3、实战
(1)、ExecutorService : 任务提交
(2)、demo
异步任务类
public class ExecutorDemo {private ExecutorService executor = Executors.newFixedThreadPool(1); public void asynTask() throws InterruptedException {executor.submit(new Runnable() {@Overridepublic void run() {try {Thread.sleep(10000); //方便观察结果} catch (InterruptedException e) {e.printStackTrace(); }int sum = 0; for(int i = 0; i < 1000; i++) {sum += i; }System.out.println(sum); }}); }}

【Java|Java 异步实现的几种方式小结】客户端模拟
public class Client {public static void main(String[] args) throws InterruptedException {boolean r = task2(); if(r) {task3(); }System.out.println("------------main end-----------"); }static boolean task2() throws InterruptedException {ExecutorDemo e = new ExecutorDemo(); e.asynTask(); System.out.println("------------task2 end-----------"); return true; }static void task3() throws InterruptedException {int j = 0; while(true) {if(j++ > 10000) {break; }}System.out.println("------------task3 end-----------"); }}

结果是酱紫的
------------task2 end-----------
------------task3 end-----------
------------main end-----------
499500
我们来分析下结果, task2是个异步任务,执行到task2,主线程不会在task2 阻塞,不用等待task2 处理完,可以往下执行task3.

    推荐阅读