最近在給別人講解Java併發編程面試考點時,爲了解釋鎖對象這個概念,想了一個形象的故事。後來慢慢發現這個故事彷佛能講解Java併發編程中好多核心概念,因而完善起來造成了了這篇文章。你們先忘記併發編程,只聽我給你講個故事。面試
故事可能比較奇怪。有這麼一個學校,裏面有好多好多人,咱們簡單分紅學生、老師、以及宿管阿姨。學校中間還有一個很奇葩的水果超市,裏面有個倉庫放着蘋果、西瓜、橘子。來這個超市的人,一方面能夠拿走水果吃掉,另外一方面也能夠送來水果還錢。不過超市還有一個很奇葩的規則,就是學生只能去吃或者送蘋果,老師則只能西瓜,宿管阿姨只能橘子。編程
這個超市的進出也頗有規矩,來這個超市的人,必須持有相應的證件,學生則須要持有學生證,老師須要持有教師證,宿管阿姨須要持有阿姨證。這三個證每一個都分別只有一個,保管在超市門口的一個領證處,人們進入這個超市以前,必須先去取證處那裏領取相應的證件才能進入。若是證件暫時被別人取走了拿不到,則須要到後方的等待區裏面排隊等證。那這個等待區也有三個,分別是學生證等待區,教師證等待區,阿姨證等待區。多線程
進入超市裏面就更加奇葩了,不管是要從這個超市拿走水果,仍是要送來水果,都須要經過一個操做臺來控制,而這個操做臺,同一時刻只能有一我的進行操做。這個操做臺爲了防止有人霸佔操做臺過長時間,只容許一我的持續操做10s鍾,10s以後會在屏幕上顯示一個ID,只有這個ID的人才能來操做,至於選擇什麼號碼,老師學生或是宿管阿姨都沒法決定和干預,只能任憑這個操做臺來決策。但好在,每一個人在操做臺上都有本身的帳號,操做一半被中斷的數據並不會丟失。併發
這個故事的背景就介紹完了,下面這個學校就發生了各類各樣的事。spa
首先咱們假設,進這所學校的人,都是爲了去超市作事情。某一時刻,操做臺上顯示了一個號碼2號,這個號碼經過各類學校大屏幕通知給全部的人。因而ID爲2號的學生小明看到了本身的號碼,得知本身得到了進入超市操做控制檯的權利,因而出發前往超市。小明首先到超市門口,問領證處的管理人員,「給我一張學生證!」。管理人員找了找發現有一張學生證,因而便給了小明。小明拿到了學生證,順利進入超市,並坐上了操做臺前,登陸了本身的帳號系統。小明此行的目的是爲了拿走一個蘋果,因而他點擊了蘋果商品的圖標,系統顯示蘋果還有4個。因而小明順利地拿走了蘋果,系統將蘋果數量-1,將新的蘋果數量3記錄到總系統庫中。接着小明走出超市,將學生證交還給了領證處,走出了校園,消失在外面的人海中。操作系統
接着操做臺上顯示了3號,一樣經過學校大屏幕通知給了全部人。ID爲3號的學生小張看到了本身的號碼,得知本身得到了進入超市操做控制檯的權利,因而出發前往超市。小張和小明作着徹底相同的操做,但小張操做太慢了,剛剛點擊完了蘋果商品的圖標,系統就顯示了下一我的的號碼5號。此時小明只能被迫終止本身的操做,讓出操做臺的權利。ID爲5號的學生小王接到通知,興沖沖地前往超市,並在領證處問管理人員,「給我一張學生證!」。管理人員找了找,發現學生證已經被小明取走了,只能告訴小王,「抱歉,學生證暫時沒有,請到後面的學生證等待區排隊吧!」。小王沒辦法,只能乖乖去排隊了。線程
這是操做臺再次顯示了2號,也就是剛剛操做到一半的小明。小明此時還在超市裏,並不須要從新進入,因而小明趕忙到操做臺前繼續着剛剛的操做,取走了一個蘋果,離開了超市,交還了學生證。此時領證處的管理人員收到了學生證,對着後面的學生證排隊區喊,「學生證有啦,排隊的人過來取吧!」。正在排隊等證的5號小王聽到後,從排隊的隊列裏出來,準備領證並進入超市。但此時操做臺上顯示的號是另外一個學生10號,10號學生拿走了學生證,進入超市開始操做。操做到一半,操做臺時間限制又到了,顯示了小王的ID5號。小王剛從等待領證的隊列裏出來,終於得到了進行下一步行動的准許,因而走向了領證處,「給我一張學生證!」,因爲學生證已經被10號拿走,管理人員只能說,「抱歉,學生證暫時沒有,請到後面的學生證等待區排隊吧!」。小王一看等了那麼久竟然又被別人搶先了一步,剛想爆粗口,想到了這個學校的名言,「這個世界是不公平的」,因而又乖乖走向了學生證等待區,繼續排隊。對象
等10號操做完出來了,還了學生證,小王又被領證處管理員喊話,「學生證有啦,排隊的人過來取吧!」。小王走出排隊區,而此時操做臺終於顯示了小王的號碼5號。小王此次順利領取了學生證,進入了超市,坐在了操做臺上,登陸了本身的系統。小王想買蘋果,因而點擊了蘋果商品的按鈕,但系統顯示蘋果數量爲0!小王此時想了想,有了個接下來的計劃:隊列
剛恰好超市旁邊爲每一種水果準備了好多等待區,一共有六個,分別是:蘋果沒了等待區,西瓜沒了等待區,橘子沒了等待區。蘋果滿了等待區,西瓜滿了等待區,橘子滿了等待區。小王很聰明,去了蘋果沒了等待區,等待着有人往裏送蘋果的信號。內存
這時小孫走進了超市,給超市添置了5個蘋果,並換來了零花錢。以後他馬上通知蘋果沒了等待區,給了個信號「超市有蘋果啦!」,但此時小孫尚未走出超市呢。小王在等待區裏收到信號,馬上走出了等待區,等待被叫號,以完成本身吃蘋果的任務。但很不幸,在小王獲得叫號機會以前,蘋果又被其餘幾個學生搶光了,這時才輪到小王。小王也很聰明,他考慮到了這種狀況,沒有直接取蘋果,而是從新查詢了一變蘋果數量,發現蘋果數量爲0,因而重複以前的步驟,小王再次回到了蘋果沒了等待區。
接下來的時間裏,小王不斷在蘋果沒了等待區和學生證等待區移動,小王發現爲了吃一個蘋果太難了,必須同時知足,蘋果沒了等待區發來了「超市有蘋果了」的信號,領證區此時有學生證,而且在操做臺上查詢出的蘋果數量不爲0。終於有一次。小王成功知足了這三個條件,在操做臺上看到蘋果的數量爲1!小王正激動地準備按下購買按鈕,可此時操做臺一閃,忽然出現了別人的號碼。這我的是超市管理員,拿着一張特殊的超市管理員證順利進入了超市,將蘋果拿走,此時蘋果數量又變成了0。以後又輪到小王操做,但小王並不知道以前發生的一切,他眼中明明看到蘋果數量是1。小王爲了保險起見,又屢次查詢了蘋果數量,發現仍然是1,因而興奮地點下了購買按鈕!因而,操做臺對根本沒有蘋果的儲藏區發出了取蘋果的指令,該系統根本沒有想到會有這種事情發生,因而機器炸了,整個學校夷爲平地。
數年後,學校慢慢被從新創建了起來,以前作操做臺的人已經被槍斃了,高薪聘請了一位高人來建造,解決了以前的那個問題。超市又順利運轉起來,有時超市只有一我的,有時超市會有三我的,分別是學生、老師、宿管阿姨,他們仨人互不影響,相安無事。學校的生活再次豐富了起來。
----------------------華麗的分割線-----------------------
這個故事包含了Java多線程的大部分核心問題,下面我把故事從新講一遍。
有這麼一個學校(Java虛擬機),裏面有好多好多人(線程),咱們簡單分紅學生、老師、以及宿管阿姨。學校中間還有一個很奇葩的水果超市(臨界區),裏面有個倉庫放着蘋果、西瓜、橘子(臨界區裏的受保護資源)。來這個超市的人,一方面能夠拿走水果吃掉,另外一方面也能夠送來水果還錢。不過超市還有一個很奇葩的規則,就是學生只能去吃或者送蘋果,老師則只能西瓜,宿管阿姨只能橘子。
這個超市的進出也頗有規矩,來這個超市的人,必須持有相應的證件(鎖對象),學生則須要持有學生證,老師須要持有教師證,宿管阿姨須要持有阿姨證(不一樣的鎖對象)。這三個證每一個都分別只有一個,保管在超市門口的一個領證處(獲取鎖的地方--能夠說是堆吧),人們進入這個超市以前,必須先去取證處那裏領取相應的證件(獲取鎖)才能進入。若是證件暫時被別人取走了拿不到(獲取鎖失敗),則須要到後方的等待區(同步隊列SychronizedQueue)裏面排隊等證。那這個等待區也有三個,分別是學生證等待區,教師證等待區,阿姨證等待區(每一個鎖對象對應一個同步隊列)。
進入超市裏面就更加奇葩了,不管是要從這個超市拿走水果,仍是要送來水果,都須要經過一個操做臺(單核CPU)來控制,而這個操做臺,同一時刻只能有一我的進行操做。這個操做臺爲了防止有人霸佔操做臺過長時間,只容許一我的持續操做10s鍾(CPU時間片),10s以後會在屏幕上顯示一個ID,只有這個ID的人才能來操做(線程切換)。至於選擇什麼號碼,老師學生或是宿管阿姨都沒法決定和干預,只能任憑這個操做臺來決策(操做系統決定線程的切換和時間的分配)。但好在,每一個人在操做臺上都有本身的帳號(線程的工做內存),操做一半被中斷的數據並不會丟失。
這個故事的背景就介紹完了,下面這個學校就發生了各類各樣的事。
首先咱們假設,進這所學校的人,都是爲了去超市作事情。首先人出如今學校外(線程狀態NEW),人進入學校(線程狀態RUNNABLE)。某一時刻,操做臺上顯示了一個號碼2號,這個號碼經過各類學校大屏幕通知給全部的人。因而ID爲2號的學生小明看到了本身的號碼,得知本身得到了進入超市操做控制檯的權利(得到CPU執行權),因而出發前往超市。小明首先到超市門口,問領證處的管理人員,「給我一張學生證!」(獲取鎖)。管理人員找了找發現有一張學生證,因而便給了小明。小明拿到了學生證,順利進入超市(獲取鎖成功,進入臨界區),並坐上了操做臺前,登陸了本身的帳號系統(準備好工做內存,開始執行臨界區代碼)。小明此行的目的是爲了拿走一個蘋果,因而他點擊了蘋果商品的圖標,系統顯示蘋果還有4個。因而小明順利地拿走了蘋果,系統將蘋果數量-1,將新的蘋果數量3記錄到總系統庫中(代碼)。接着小明走出超市(代碼執行完畢出臨界區),將學生證交還給了領證處(釋放鎖),走出了校園(線程狀態TERMINAL),消失在外面的人海中。
接着操做臺上顯示了3號,一樣經過學校大屏幕通知給了全部人。ID爲3號的學生小張看到了本身的號碼,得知本身得到了進入超市操做控制檯的權利,因而出發前往超市。小張和小明作着徹底相同的操做,但小張操做太慢了,剛剛點擊完了蘋果商品的圖標,系統就顯示了下一我的的號碼5號。此時小明只能被迫終止本身的操做,讓出操做臺的權利(線程切換)。ID爲5號的學生小王接到通知,興沖沖地前往超市,並在領證處問管理人員,「給我一張學生證!」。管理人員找了找,發現學生證已經被小明取走了,只能告訴小王,「抱歉,學生證暫時沒有,請到後面的學生證等待區(同步隊列WaitQueue)排隊吧!」(獲取鎖失敗)。小王沒辦法,只能乖乖去排隊了(線程狀態BLOCKING)。
這是操做臺再次顯示了2號,也就是剛剛操做到一半的小明。小明此時還在超市裏(不釋放鎖),並不須要從新進入,因而小明趕忙到操做臺前繼續着剛剛的操做(線程切換,繼續執行中斷的代碼),取走了一個蘋果,離開了超市,交還了學生證(釋放鎖)。此時領證處的管理人員收到了學生證,對着後面的學生證排隊區喊,「學生證有啦,排隊的人過來取吧!」(通知同步隊列出隊)。正在排隊等證的5號小王聽到後,從排隊的隊列裏出來,準備領證並進入超市。但此時操做臺上顯示的號是另外一個學生10號,10號學生拿走了學生證,進入超市開始操做。操做到一半,操做臺時間限制又到了,顯示了小王的ID5號。小王剛從等待領證的隊列裏出來,終於得到了進行下一步行動的准許,因而走向了領證處,「給我一張學生證!」,因爲學生證已經被10號拿走,管理人員只能說,「抱歉,學生證暫時沒有,請到後面的學生證等待區排隊吧!」。小王一看等了那麼久竟然又被別人搶先了一步,剛想爆粗口,想到了這個學校的名言,「這個世界是不公平的」,因而又乖乖走向了學生證等待區,繼續排隊。(非公平鎖,並非誰等的時間最長誰就獲取鎖)
等10號操做完出來了,還了學生證,小王又被領證處管理員喊話,「學生證有啦,排隊的人過來取吧!」。小王走出排隊區,而此時操做臺終於顯示了小王的號碼5號。小王此次順利領取了學生證,進入了超市,坐在了操做臺上,登陸了本身的系統。小王想買蘋果,因而點擊了蘋果商品的按鈕,但系統顯示蘋果數量爲0!小王此時想了想,有了個接下來的計劃:
剛恰好超市旁邊爲每一種水果準備了好多等待區(等待隊列WaitQueue),一共有六個,分別是:蘋果沒了等待區,西瓜沒了等待區,橘子沒了等待區。蘋果滿了等待區,西瓜滿了等待區,橘子滿了等待區(條件變量Condition)。小王很聰明,走出超市交還學生證(wait會釋放鎖),去了蘋果沒了等待區(wait),等待着有人往裏送蘋果的信號(同步信號-喚醒)。
這時小孫走進了超市,給超市添置了5個蘋果,並換來了零花錢。以後他馬上通知蘋果沒了等待區,給了個信號「超市有蘋果啦!(AppleNotEmpty.notifyAll)」,但此時小孫尚未走出超市呢(notify不釋放鎖)。小王在等待區裏收到信號,馬上走出了等待區,等待被叫號,以完成本身吃蘋果的任務。但很不幸,在小王獲得叫號機會以前,蘋果又被其餘幾個學生搶光了,這時才輪到小王。小王也很聰明,他考慮到了這種狀況,沒有直接取蘋果,而是從新查詢了一變蘋果數量(wait通常配合while條件),發現蘋果數量爲0,因而重複以前的步驟,小王再次回到了蘋果沒了等待區。
接下來的時間裏,小王不斷在蘋果沒了等待區和學生證等待區移動,小王發現爲了吃一個蘋果太難了,必須同時知足,蘋果沒了等待區發來了「超市有蘋果了」的信號,領證區此時有學生證,而且在操做臺上查詢出的蘋果數量不爲0。終於有一次。小王成功知足了這三個條件,在操做臺上看到蘋果的數量爲1!小王正激動地準備按下購買按鈕,可此時操做臺一閃,忽然出現了別人的號碼。這我的是超市管理員,拿着一張特殊的超市管理員證順利進入了超市,將蘋果拿走,此時蘋果數量又變成了0。以後又輪到小王操做,但小王並不知道以前發生的一切,他眼中明明看到蘋果數量是1。小王爲了保險起見,又屢次查詢了蘋果數量,發現仍然是1(非volatile修飾的變量不保證線程之間的可見性),因而興奮地點下了購買按鈕!因而,操做臺對根本沒有蘋果的儲藏區發出了取蘋果的指令,該系統根本沒有想到會有這種事情發生,因而機器炸了,小王犧牲(拋出運行時異常,線程釋放鎖並終止)。
數年後,以前作操做臺的人已經被槍斃了,學校又高薪聘請了一位高人來建造,解決了以前的那個問題(volatile)。超市又順利運轉起來,有時超市只有一我的(不一樣線程進入鎖對象相同的臨界區會互斥,只有一個線程能夠進入),有時超市會有三我的(不一樣鎖對象的臨界區不互斥),分別是學生、老師、宿管阿姨,他們仨人互不影響,相安無事。學校的生活再次豐富了起來。
故事講完了,雖然不能解釋所有併發編程的內容,也不能到處都很恰當地說明細節,但確是一個頗有趣的思考過程,但願你們也能積極討論下故事中的錯誤和不完善的地方,一塊兒將故事講的更好。下面整理一下故事中出現的東西和寓意。
東西 | 寓意 |
人 | 線程 |
通行證 | 鎖對象 |
水果超市 | 臨界區代碼 |
水果 | 受保護資源 |
操做臺 | CPU |
叫號 | 時間片分配 |
領證處 | 獲取鎖 |
等待區 | 等待隊列 |
領證排隊區 | 同步隊列 |
水果儲藏區 | 主內存 |
每一個人的帳號系統 | 工做內存 |