Java 异步编程

  昨天头儿给的学习文档我还没看完,头儿说:“MongoDB光会简单的添删改查什么的不行,要深刻了解,大家连$set和$inc使用场景都分不清。”java

  确实,学习过一年多SQL,确实对学习MongoDB有点影响。数据库

  不过,今天数据库的事情先翻过去,由于我在学习文档中还看到了另一个加大加粗的标题——异步编程编程

  Java在Java8以前貌似(由于我也刚学,因此不对还请各位前辈指正)没有真正实现异步编程的方法,当时异步编程会使用回调或者使用其余的框架(如Netty和Guava)来实现。后来Java8借鉴了不少框架的思想,能够借助JDK原生的CompletableFuture来实现异步操做,并且用Lambda表达式来写匿名内部类大大简化了代码量。多线程

  那么,异步编程究竟是用来干什么的呢?并发

  试想这样的场景——老爸有俩孩子:小红和小明。老爸想喝酒了,他让小红去买酒,小红出去了。而后老爸忽然想吸烟了,因而老爸让小明去买烟。在面对对象的思想中,通常会把买东西,而后买回来这件事做为一个方法,若是按照顺序结构或者使用多线程同步的话,小明想去买烟就必须等小红这个买东西的操做进行完。这样无疑增长了时间的开销。异步就是为了解决这样的问题。你能够分别给小红小明下达指令,让他们去买东西,而后你就能够本身作本身的事,等他们买回来的时候接收结果就能够了。框架

  因此,为了尽快学习异步操做,我使用Java8的方法编写了这样一段代码来帮助了解:异步

 1 package com.liumaowu;
 2 
 3 import java.util.concurrent.CompletableFuture;
 4 import java.util.concurrent.ExecutorService;
 5 import java.util.concurrent.Executors;
 6 
 7 public class TestFuture {
 8     public static void main(String[] args) {
 9         //两个线程的线程池
10         ExecutorService executor= Executors.newFixedThreadPool(2);
11         //小红买酒任务,这里的future2表明的是小红将来发生的操做,返回小红买东西这个操做的结果
12         CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()-> {
13             System.out.println("爸:小红你去买瓶酒!");
14             try {
15                 System.out.println("小红出去买酒了,女孩子跑的比较慢,估计5s后才会回来...");
16                 Thread.sleep(5000);
17                 return "我买回来了!";
18             } catch (InterruptedException e) {
19                 System.err.println("小红路上遭遇了不测");
20                 return "来世再见!";
21             }
22         },executor);
23 
24         //小明买烟任务,这里的future1表明的是小明将来买东西会发生的事,返回值是小明买东西的结果
25 CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->{ 26 System.out.println("爸:小明你去买包烟!"); 27 try { 28 System.out.println("小明出去买烟了,可能要3s后回来..."); 29 Thread.sleep(3000); 30 return "我买回来了!"; 31 } catch (InterruptedException e) { 32 System.out.println("小明路上遭遇了不测!"); 33 return "这是我托人带来的口信,我已经不在了。"; 34 } 35 },executor); 36 37 //获取小红买酒结果,从小红的操做中获取结果,把结果打印 38 future2.thenAccept((e)->{System.out.println("小红说:"+e);}); 39 //获取小明买烟的结果 40 future1.thenAccept((e)->{System.out.println("小明说:"+e);}); 41 42 System.out.println("爸:loading......"); 43 System.out.println("爸:我以为无聊甚至去了趟厕所。"); 44 System.out.println("爸:loading......"); 45 } 46 }

  这个运行结果以下:异步编程

爸:小红你去买瓶酒!
小红出去买酒了,女孩子跑的比较慢,估计5s后才会回来...
爸:小明你去买包烟!
小明出去买烟了,可能要3s后回来...
爸:loading......
爸:我以为无聊甚至去了趟厕所。
爸:loading......
小明说:我买回来了!
小红说:我买回来了!

  由于sleep时间调的挺大,因此能够很明显的看到小明买回来的结果是在等待以后一下子才跳出来,小红买回来的结果也等待了一下子才跳出来。由于是先让小红买,再让小明出去的,可是小明的结果先回来,这说明何时取到结果取决于操做的时间长度。学习

  以后我把取futrue的结果的操做放到了老爸等待语句的下面,以下:spa

 1 /*
 2 上面的代码不动
 3 */
 4         System.out.println("爸:loading......");
 5         System.out.println("爸:我以为无聊甚至去了趟厕所。");
 6         System.out.println("爸:loading......");
 7         //获取小红买酒结果
 8         future2.thenAccept((e)->{System.out.println("小红说:"+e);});
 9         //获取小明买烟的结果
10         future1.thenAccept((e)->{System.out.println("小明说:"+e);});

  屡次运行以后结果就比较有意思了:

结果1:结果2:结果3

  这种解决异步的方式仍是使用到了多线程,在获取结果以前,操做的顺序取决于线程操做到了哪一步,好比结果2,由于小红线程启动的慢了,因此小明先出去了。可是若是先取结果的话,会确保这个线程已经启动了,因此老爸loading始终显示在老爸发出买东西命令以后。这也算先强制线程启动,再进行主操做。

 

在看了异步以后,不可避免地会把同步/异步/多线程弄混淆,那么咱们再来总结下几个东西的区别

通常状况:顺序结构,必须等待前面的操做完成(两我的说话,a把全部话说完,b才能继续说)

并发:同一时间段处理多个任务的能力(两人说话,支持你一言我一语的交流,两人在一个时间段内都有说话,是基于时间段内的同时发生)

并发又有同步和互斥

  互斥:不能同时使用临界资源(有一个共享资源--话筒,两人必须用话筒说话,但同时只能有一我的用这个话筒,保证了只有一我的在说话)

   同步:前一个处理的结果做为下一个处理的资源。大多数状况下,同步已经实现了互斥。(两人你一言我一语的交流,我必须知道你说了啥我才能接上你的话)

并行:同一时刻处理多个任务的能力(两人合唱,同时出声)

异步:不用等待一个结果出来,能够继续其余操做(两我的不说话了,寄信,a把信拿到邮局就不用管了,回家能够想干吗就干吗,等b回信到了,取邮局接收一下结果--b的回信就能够了)

多线程:若是说同步和异步是对如何处理事情的要求,那么多线程就是实现这些要求的方法