好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

Java 异步实现的几种方式

1. jdk1.8之前的Future

jdk并发包里的Future代表了未来的某个结果,当我们向线程池中提交任务的时候会返回该对象,可以通过future获得执行的结果,但是jdk1.8之前的Future有点鸡肋,并不能实现真正的异步,需要阻塞的获取结果,或者不断的轮询。

通常我们希望当线程执行完一些耗时的任务后,能够自动的通知我们结果,很遗憾这在原生jdk1.8之前是不支持的,但是我们可以通过第三方的库实现真正的异步回调。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

/**

  * jdk1.8之前的Future

  * @author Administrator

  */

public class JavaFuture {

     public static void main(String[] args) throws Throwable, ExecutionException {

         ExecutorService executor = Executors.newFixedThreadPool( 1 );

         Future<String> f = executor.submit( new Callable<String>() {

 

             @Override

             public 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这种方式有点儿类似下面的过程:

2. jdk1.8开始的Future

直到jdk1.8才算真正支持了异步操作,jdk1.8中提供了lambda表达式,使得java向函数式语言又靠近了一步。借助jdk原生的CompletableFuture可以实现异步的操作,同时结合lambada表达式大大简化了代码量。代码例子如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

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 {

                     //模拟耗时操作

                     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" );

     }

}

实现方式类似下图:

3. Spring的异步方法

先把longTimeMethod 封装到Spring的异步方法中,这个异步方法的返回值是Future的实例。这个方法一定要写在Spring管理的类中,注意注解@Async。

?

1

2

3

4

5

6

7

8

@Service

public class AsynchronousService{

   @Async

   public Future springAsynchronousMethod(){

     Integer result = longTimeMethod();

     return new AsyncResult(result);

   }

}

其他类调用这个方法。这里注意,一定要其他的类,如果在同类中调用,是不生效的。

?

1

2

3

4

5

6

@Autowired

private 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

异步任务类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

public class ExecutorDemo {

     private ExecutorService executor = Executors.newFixedThreadPool( 1 );   

     public void asynTask() throws InterruptedException {   

         executor.submit( new Runnable() {           

             @Override

             public 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);

             }

         });       

     }

}

客户端模拟

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

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.

原文链接:https://blog.csdn.net/weixin_43525116/article/details/85698872

查看更多关于Java 异步实现的几种方式小结的详细内容...

  阅读:35次