對於多線程的解釋有很多書籍和文章,但是都不能通俗易懂的讓多數人快速理解,主要就是舉得例子未能完全貼合實際生活,既然Java是面向對象,它的所有設計和思想都可以從現實生活中找到答案,我也是很不喜歡很多書籍一上來就寫的跟天書一樣的晦澀難懂,尤其官方文檔更不用說了,當然是為了嚴謹,但是我們在學習java理解的時候,必須要做到知其然並知其所以然,而不是記一個標準答案,或者死記硬背,這樣是無法達到知命境界的。
一、成為修行者
從下面開始我們進入一個全新的世界來理解java的多線程
首先你只是一個凡人,但是機緣巧合之下,你被java的一個主線程附體,變成了修行者,達到了初始境界,看著這個世界來了一句,hello world。
<code>public class Me { public static void main(String[] args) { System.out.println("hello world"); } }/<code>
二、創建多線程
雖然成了修行者還是得吃飯,可是傳統的做飯一個人只能一樣樣的做,可不可以同時完成這些工作呢,比如一邊洗菜,一邊炒菜,一邊淘米,一邊煮飯,而這些如何能同時進行呢?
這時候你翻看java卷天書,從而發現了以下一段秘籍,你創建了多線程,來解決了這個問題
<code>/** * @author xiaomianyang * @description * @date 2020-02-06 12:30 */ public class Me { public static void main(String[] args) throws Exception{ String worker[]={"我是洗菜的","我是炒菜的","我是淘米的","我是煮飯的"}; for(int i=0;i/<code>
三、線程為什麼不按順序執行
你分身出來四個人都開始幫你幹活了,可是發現一個問題,他們沒有按照順序執行自己的工作,無論執行多少次都會發現執行順序總是在發生變化。
為什麼沒有按照順序執行呢,理想中不是誰先start,誰先開始嗎?為啥會這樣呢?首先從面向對象角度來解釋,因為這四個分身都是你分出來的,雖然看上去這四個分身都擁有自己的想法,看上去四個是同時在做事情,實際上他們用的都是你這一個大腦,只是作為修行者,大腦控制頻率切換比較快,看上去四個人都是同時在幹活,實際上在同一時刻大腦只能控制一個人在工作,屬於併發行為,而感官上他們似乎是同時發生的,從計算機角度也是一樣的,你的大腦相當於CPU在真正的同一時刻只有一個線程再跑,只是CPU切換比較快,所以感覺好像是並行實則是併發運行,所以他們不能按順序就是因為CPU在隨機切換。
如何解決這個問題呢,既然是你創建的分身你肯定希望它按照自己的執行順序執行,於是在翻看天書發現找到了join指令,那你就給每個分身說,你幹完了他在幹活。
join就是阻塞當前線程,直到當前線程執行完後才執行下一個線程。
<code>public class Me { public static void main(String[] args) throws Exception{ String worker[]={"我是洗菜的","我是炒菜的","我是淘米的","我是煮飯的"}; for(int i=0;i/<code>
四、可重入鎖可以幹什麼
的確達到想要的效果了,你分身出來了四個人同時在幫你幹活,而你什麼都不用做,可是你發現炒菜的沒有菜卻在炒菜,煮米的沒有米卻在煮米,因為洗菜的還把菜沒有洗好,而淘米的也沒有弄好米,這就導致炒菜的和煮米的在幹無意義的工作,而且還浪費水浪費火,這不就是浪費資源嗎?那麼問題來了,怎麼讓洗完菜通知炒菜的,洗完米的通知煮飯的呢?這就涉及到了線程之間的通信,讓線程互相交流而合理完成工作,我們可以利用可重入鎖實現這個功能
要利用可重入鎖,我們創建四個線程,採用java8的新語法創建,這裡我們就讓這四個線程協調通信完成工作,那為啥不用join,因為join是阻塞式的,事實上,洗菜和淘米是可以同時進行的,但是用了join,就得等前面的一個個執行完,那麼利用可重入鎖就可以很好的解決這個問題了
對於鎖的釋放要寫在finally裡面,防止死鎖
<code>import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author xiaomianyang * @description * @date 2020-02-06 12:30 */ public class Me { private static Lock lock = new ReentrantLock(); private static Lock lock2 = new ReentrantLock(); private static Condition condition1 = lock.newCondition(); private static Condition condition2 = lock2.newCondition(); private static Boolean one=false; private static Boolean three=false; public static void main(String[] args){ new Thread(() -> { //加鎖,執行完成後發送條件信號 lock.lock(); System.out.println("我是洗菜的"); one=true; condition1.signal(); lock.unlock(); }).start(); new Thread(() -> { try { lock.lock(); //如果一號沒有執行完成就繼續等待 否則 執行任務並釋放鎖 if (!one) { condition1.await(); } System.out.println("我是炒菜的"); }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); } }).start(); new Thread(() -> { lock2.lock(); System.out.println("我是淘米的"); three=true; condition2.signal(); lock2.unlock(); }).start(); new Thread(() -> { try { lock2.lock(); if (!three) { condition2.await(); } System.out.println("我是煮飯的"); }catch(InterruptedException e){ e.printStackTrace(); }finally { lock2.unlock(); } }).start(); } }/<code>
查看輸出結果,的確按照我們想要的方式執行了
五、線程的停止
比如分身中的其中一個在炒菜,炒了五盤菜的時候,我不想讓他炒了,那就得把他停止掉,該如何停止掉呢?
可以使用interrupt對當前線程終止,但是這個方法並不會立即終止該線程,所以我們使用Thread.currentThread().isInterrupted()判斷是否有中斷信號,以此來停止當前線程,這樣程序就會break跳出循環,程序的不執行也就是真正意義上的線程終止
<code>/<code>
那麼再來一個問題,如果我想讓煮米的人發現炒菜的人炒了10盤菜後,告訴炒菜的人停止炒菜該怎麼做呢?