昨天頭兒給的學習文檔我還沒看完,頭兒說:「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的回信就能夠了)
多線程:若是說同步和異步是對如何處理事情的要求,那麼多線程就是實現這些要求的方法。