您好,我是湘王,這是我的頭條号「湘王說」,歡迎您來,歡迎您再來~
從事Java開發這些年來,如果要問我Java當中最難的部分是什麼?最有意思的部分是什麼?最多人讨論的部分是什麼?那我會毫不猶豫地說:多線程。
之前說過I/O是比較裹人概念之二,那麼多線程就是那個之一,除了它也沒誰了!如果一定要找另外一個特性,那就非JVM莫屬吧。
涉及Java多線程的書籍、文章、日志,包括面試問題,非常非常非常多,但真正能深刻了解并掌握這個有趣特性的人,很少(我并不是這部分很少的人之一,隻是能有幸站在前人的肩膀上用自己一點淺薄的經驗來發表見解)。
Java多線程說它難,也不難,就是有點繞;說它簡單,也不簡單,需要了解的概念很多,尤其是很多底層知識,如資料結構、作業系統的部分。
Java多線程掌握得好,不僅僅隻是對Java,對任何其他具有并發特性的程式設計語言,甚至是作業系統,都能有更全面和準确的認識。
Java多線程最大的特點,而且也是唯一确定的一件事,那就是:在多線程環境下,程式的運作結果是無法預料的,但這也正是它最有趣的地方。
在了解多線程之前,最好先知道什麼是并發,什麼是并行(這在我之前寫的《CPU的分身術:千手觀音與齊天大聖》裡面有提到過)。不然很容易迷糊。
總的來說,就是這樣:
并行:同一時刻可以同時發生/執行多個任務
并發:同一時刻隻能發生/執行一個任務
學習多線程最好從如下六個方面循序漸進(純粹個人經驗和建議,可無視):
1、線程生命周期:NEW、RUNNABLE(READY、RUNNING)、BLOCKED、WAITING、TIMED_WAITING、TERMINATED狀态
2、關鍵字:synchronized和volatile
3、線程池:ThreadPoolExecutor
4、鎖(AQS):悲觀鎖/樂觀鎖、輕量級鎖/重量級鎖、自旋鎖/可重入鎖等各種鎖
5、CAS:各種原子類
6、并發工具類:ArrayBlockingQueue、CountDownLatch、CyclicBarrier、Semaphore等
就如同每個人在不同的年齡階段有不同的特征和生活狀态一樣,也可以用“生命周期”來描述線程在不同階段下的不同特征。線程生命周期涉及到不同線程狀态間的轉換,這種狀态變換,最早可以追溯到《作業系統》中的PV操作與信号燈。PV操作是一種實作程序互斥與同步的方法,P(passeren)表示通過,V(vrijgeven)表示釋放。信号燈其實就是交通信号燈,為了避免出現沖突(交通事故),同時更有效地利用公路資源,通過信号燈來實作交通管理:
再說回Java,Java多線程用一句話總結就是「6類5法」。
所謂「6類」,就是多狀态的狀态分為這6類:
1、建立(NEW):新建立了一個線程,但還沒調用start方法
2、運作(RUNNABLE)
2.1、就緒(ready):運作start方法後,線程位于可運作線程池中,等待被排程
2.2、運作中(RUNNING):就緒的線程獲得CPU的時間片就變為運作中
3、阻塞(BLOCKED):線程等待擷取鎖
4、等待(WAITING):接收事件通知後或系統中斷後進入等待
5、逾時(TIMED_WAITING):等待指定時間後會自行傳回
6、終止(TERMINATED):線程已執行完畢
這是線程生命周期的狀态變化圖:
簡單來說,就是這樣:
而所謂「5法」就是線程的核心方法是這麼5個:
1、wait:目前線程調用鎖對象的wait方法,目前線程釋放鎖,進入等待狀态,由其他線程接着執行
2、notify/notifyAll:喚醒任意一個或全部等待的線程後接着執行,但并不釋放鎖
3、join:目前線程調用其他線程的join方法,調用後目前線程進入等待狀态
4、yield:目前線程調用,調用後暫停執行(可能無效),變為就緒态
5、sleep:目前線程調用,調用後進入TIME_WAITING狀态
用代碼來解釋一下會更直覺一些。
第一種wait/notify的情況:
此時代碼執行流程(兩種可能):
1、T1先執行
1.1、T1啟動,wait讓出鎖,讓出CPU,T2獲得CPU,T2啟動,notify了通過object鎖等待的線程
1.2、T1被喚醒後等待啟動,T2繼續執行,T2執行完,T1獲得CPU後繼續執行
2、T2先執行
T2執行完,T1啟動,讓出CPU,由于沒有線程再來執行notify,程式無限期等待
這裡要強調的重點是:
1、wait會讓出CPU而notify不會
2、wait重點在通知其它同用一個object的線程“我暫時不用了”,并且讓出CPU
3、notify重點在于通知使用object的對象“我用完了!”
如果說隻有兩個線程的時候,還能嘗試着分析一下結果,那麼當有四個線程的時候會如何呢?看看代碼:
然後同時開啟這四個線程,但結果是無法預料!為什麼?因為隻有兩種可能的流程(要麼wait先執行完,要麼notify先執行完),至于每種流程裡面怎麼執行的?不知道!不清楚!無法預料!這就是多線程讓人困惑的地方和魅力所在。
而且線程還有一個無賴的行為就是:雖然你有優先級,但我不保證有用!
這裡不管怎麼設定t1或者t2的優先級,都沒有用,運作的結果每次都可能不一樣。
線程的生命周期6類5法算是比較簡單的,是基礎中的基礎。但是用好很難,關鍵在于多練多想,多多嘗試各種組合。
感謝您的大駕光臨!咨詢技術、産品、營運和管理相關問題,請關注後留言。歡迎騷擾,不勝榮幸~
我在頭條