同步和異步一般來形容一次方法調用,同步方法調用一旦開始,調用者必須等到方法調用返回後,才能繼續後續的行爲。異步方法調用更像一個消息傳遞,一旦開始,方法調用就會當即返回,調用者就能夠繼續後續的操做。而異步方法一般會在另一個線程中「真實」地執行。整個過程,不會阻礙調用者的工做。java
如圖:
多線程
上圖中顯示了同步方法調用和異步方法調用的區別。對於調用者來講,異步調用彷佛是一瞬間就完成的。若是異步調用須要返回結果,那麼當這個異步調用真實完成時,則會通知調用者。併發
打個比方,好比購物,若是你去商場買空調,當你到了商場看重了一款空調,你就向售貨員下單。售貨員去倉庫幫你調配物品。這天你熱的是在不行了,就催着商家趕忙給你送貨,因而你就在商店裏面候着他們,直到商家把你和空調一塊兒送回家,一次愉快的購物就結束了。這就是同步調用。異步
不過,若是咱們趕時髦,就坐在家裏打開電腦,在電腦上訂購了一臺空調。當你完成網上支付的時候,對你來講購物過程已經結束了。雖然空調尚未送到家,可是你的任務已經完成了。商家接到你的訂單後,就會加緊安平送貨,固然這一切已經跟你無關了。你已經支付完成,想幹什麼就能去幹什麼,出去溜幾圈都不成問題,等送貨上門的時候,接到商家的電話,回家一趟簽收就完事了。這就是異步調用。jvm
併發和並行是兩個很是容易被混淆的概念。他們均可以表示兩個或者多個任務一塊兒執行,可是側重點有所不一樣。併發偏重於多個任務交替執行,而多個任務之間有可能仍是串行的,而並行是真正意義上的「同時執行」,下圖很好地詮釋了這點。async
你們排隊在一個咖啡機上接咖啡,交替執行,是併發;兩臺咖啡機上面接咖啡,是並行。ide
從嚴格意義上來講,並行的多任務是真的同時執行,而對於併發來講,這個過程只是交替的,一會執行任務A,一會執行任務B,系統會不停地在二者之間切換。但對於外部觀察者來講,即便多個任務之間是串行併發的,也會形成多任務間並行執行的錯覺。高併發
併發說的是在一個時間段內,多件事情在這個時間段內交替執行。this
並行說的是多件事情在同一個時刻同事發生。操作系統
實際上,若是系統內只有一個CPU,而使用多進程或者多線程任務,那麼真實環境中這些任務不多是真實並行的,畢竟一個CPU一次只能執行一條指令,在這種狀況下多進程或者多線程就是併發的,而不是並行的(操做系統會不停地切換多任務)。真實的並行也只可能出如今擁有多個CPU的系統中(好比多核CPU)。
臨界區用來表示一種公共資源或者說共享數據,能夠被多個線程使用,可是每一次只能有一個線程使用它,一旦臨界區資源被佔用,其餘線程要想使用這個資源就必須等待。
好比,一個辦公室裏有一臺打印機,打印機一次只能執行一個任務。若是小王和小明同時須要打印文件,很明顯,若是小王先發了打印任務,打印機就開始打印小王的文件,小明的任務就只能等待小王打印結束後才能打印,這裏的打印機就是一個臨界區的例子。
在並行程序中,臨界區資源是保護的對象,若是意外出現打印機同時執行兩個任務的狀況,那麼最有可能的結果就是打印出來的文件是損壞的文件,它既不是小王想要的,也不是小明想要的。
阻塞和非阻塞一般用來形容不少線程間的相互影響。好比一個線程佔用了臨界區資源,那麼其餘全部須要這個資源的線程就必須在這個臨界區中等待。等待會致使線程掛起,這種狀況就是阻塞。此時,若是佔用資源的線程一直不肯意釋放資源,那麼其餘線程阻塞在這個臨界區上的線程都不能工做。
非阻塞的意思與之相反,它強調沒有一個線程能夠妨礙其餘線程執行,全部的線程都會嘗試不斷向前執行。
死鎖、飢餓和活鎖都屬於多線程的活躍性問題。若是發現上述幾種狀況,那麼相關線程就再也不活躍,也就是說它可能很難再繼續往下執行了。
死鎖應該是最糟糕的一種狀況了(固然,其餘幾種狀況也好不到哪裏去),以下圖顯示了一個死鎖的發生:
A、B、C、D四輛小車都在這種狀況下都沒法繼續行駛了。他們彼此之間相互佔用了其餘車輛的車道,若是你們都不肯意釋放本身的車道,那麼這個情況將永遠持續下去,誰都不可能經過,死鎖是一個很嚴重的而且應該避免和實時當心的問題,後面的文章中會作更詳細的討論。
飢餓是指某一個或者多個線程由於種種緣由沒法得到所要的資源,致使一直沒法執行。好比它的優先級可能過低,而高優先級的線程不斷搶佔它須要的資源,致使低優先級線程沒法工做。在天然界中,母雞給雛鳥餵食很容易出現這種狀況:因爲雛鳥不少,食物有限,雛鳥之間的事務競爭可能很是厲害,常常搶不到事務的雛鳥有可能被餓死。線程的飢餓很是相似這種狀況。此外,某一個線程一直佔着關鍵資源不放,致使其餘須要這個資源的線程沒法正常執行,這種狀況也是飢餓的一種。於死鎖想必,飢餓仍是有可能在將來一段時間內解決的(好比,高優先級的線程已經完成任務,再也不瘋狂執行)。
活鎖是一種很是有趣的狀況。不知道你們是否遇到過這麼一種場景,當你要作電梯下樓時,電梯到了,門開了,這是你正準備出去。但很不巧的是,門外一我的當着你的去路,他想進來。因而,你很禮貌地靠左走,禮讓對方。同時,對方也很是禮貌的靠右走,但願禮讓你。結果,大家倆就又撞上了。因而乎,大家都意識到了問題,但願儘快避讓對方,你當即向右邊走,同時,他當即向左邊走。結果,又撞上了!不過介於人類的智慧,我相信這個動做重複兩三次後,你應該能夠順利解決這個問題。由於這個時候,你們都會本能地對視,進行交流,保證這種狀況再也不發生。但若是這種狀況發生在兩個線程之間可能就不那麼幸運了。若是線程智力不夠。且都秉承着「謙讓」的原則,主動將資源釋放給他人使用,那麼久會致使資源不斷地在兩個線程間跳動,而沒有一個線程能夠同時拿到全部資源正常執行。這種狀況就是活鎖。
package com.jvm.visualvm; /** * <a href="http://www.itsoku.com/archives">Java乾貨鋪子,只生產乾貨,公衆號:javacode2018</a> */ public class Demo4 { public static void main(String[] args) { Obj1 obj1 = new Obj1(); Obj2 obj2 = new Obj2(); Thread thread1 = new Thread(new SynAddRunalbe(obj1, obj2, 1, 2, true)); thread1.setName("thread1"); thread1.start(); Thread thread2 = new Thread(new SynAddRunalbe(obj1, obj2, 2, 1, false)); thread2.setName("thread2"); thread2.start(); } /** * 線程死鎖等待演示 */ public static class SynAddRunalbe implements Runnable { Obj1 obj1; Obj2 obj2; int a, b; boolean flag; public SynAddRunalbe(Obj1 obj1, Obj2 obj2, int a, int b, boolean flag) { this.obj1 = obj1; this.obj2 = obj2; this.a = a; this.b = b; this.flag = flag; } @Override public void run() { try { if (flag) { synchronized (obj1) { Thread.sleep(100); synchronized (obj2) { System.out.println(a + b); } } } else { synchronized (obj2) { Thread.sleep(100); synchronized (obj1) { System.out.println(a + b); } } } } catch (InterruptedException e) { e.printStackTrace(); } } } public static class Obj1 { } public static class Obj2 { } }
運行上面代碼,能夠經過jstack查看到死鎖信息:
"thread2" #13 prio=5 os_prio=0 tid=0x0000000029225000 nid=0x3c94 waiting for monitor entry [0x0000000029c9f000] java.lang.Thread.State: BLOCKED (on object monitor) at com.jvm.visualvm.Demo4$SynAddRunalbe.run(Demo4.java:50) - waiting to lock <0x00000007173d40f0> (a com.jvm.visualvm.Demo4$Obj1) - locked <0x00000007173d6310> (a com.jvm.visualvm.Demo4$Obj2) at java.lang.Thread.run(Thread.java:745) Locked ownable synchronizers: - None "thread1" #12 prio=5 os_prio=0 tid=0x0000000029224800 nid=0x6874 waiting for monitor entry [0x0000000029b9f000] java.lang.Thread.State: BLOCKED (on object monitor) at com.jvm.visualvm.Demo4$SynAddRunalbe.run(Demo4.java:43) - waiting to lock <0x00000007173d6310> (a com.jvm.visualvm.Demo4$Obj2) - locked <0x00000007173d40f0> (a com.jvm.visualvm.Demo4$Obj1) at java.lang.Thread.run(Thread.java:745) Locked ownable synchronizers: - None
thread1持有com.jvm.visualvm.Demo4$Obj1的鎖,等待獲取com.jvm.visualvm.Demo4$Obj2的鎖
thread2持有com.jvm.visualvm.Demo4$Obj2的鎖,等待獲取com.jvm.visualvm.Demo4$Obj1的鎖,兩個線程相互等待獲取對方持有的鎖,出現死鎖。
package com.jvm.jconsole; import java.util.concurrent.*; /** * <a href="http://www.itsoku.com/archives">Java乾貨鋪子,只生產乾貨,公衆號:javacode2018</a> */ public class ExecutorLock { private static ExecutorService single = Executors.newSingleThreadExecutor(); public static class AnotherCallable implements Callable<String> { @Override public String call() throws Exception { System.out.println("in AnotherCallable"); return "annother success"; } } public static class MyCallable implements Callable<String> { @Override public String call() throws Exception { System.out.println("in MyCallable"); Future<String> submit = single.submit(new AnotherCallable()); return "success:" + submit.get(); } } public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable task = new MyCallable(); Future<String> submit = single.submit(task); System.out.println(submit.get()); System.out.println("over"); single.shutdown(); } }
執行代碼,輸出:
in MyCallable
使用jstack命令查看線程堆棧信息:
"pool-1-thread-1" #12 prio=5 os_prio=0 tid=0x0000000028e3d000 nid=0x58a4 waiting on condition [0x00000000297ff000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x0000000717921bf0> (a java.util.concurrent.FutureTask) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:429) at java.util.concurrent.FutureTask.get(FutureTask.java:191) at com.jvm.jconsole.ExecutorLock$MyCallable.call(ExecutorLock.java:25) at com.jvm.jconsole.ExecutorLock$MyCallable.call(ExecutorLock.java:20) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Locked ownable synchronizers: - <0x00000007173f2690> (a java.util.concurrent.ThreadPoolExecutor$Worker) "main" #1 prio=5 os_prio=0 tid=0x00000000033e4000 nid=0x5f94 waiting on condition [0x00000000031fe000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007173f1d48> (a java.util.concurrent.FutureTask) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:429) at java.util.concurrent.FutureTask.get(FutureTask.java:191) at com.jvm.jconsole.ExecutorLock.main(ExecutorLock.java:32) Locked ownable synchronizers: - None
堆棧信息結合圖中的代碼,能夠看出主線程在32行處於等待中,線程池中的工做線程在25行處於等待中,等待獲取結果。因爲線程池是一個線程,AnotherCallable得不到執行,而被餓死,最終致使了程序死鎖的現象。
java高併發系列交流羣: