從修行者角度理解java中的多線程

對於多線程的解釋有很多書籍和文章,但是都不能通俗易懂的讓多數人快速理解,主要就是舉得例子未能完全貼合實際生活,既然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>

三、線程為什麼不按順序執行

你分身出來四個人都開始幫你幹活了,可是發現一個問題,他們沒有按照順序執行自己的工作,無論執行多少次都會發現執行順序總是在發生變化。

從修行者角度理解java中的多線程

為什麼沒有按照順序執行呢,理想中不是誰先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>

查看輸出結果,的確按照我們想要的方式執行了

從修行者角度理解java中的多線程

五、線程的停止

比如分身中的其中一個在炒菜,炒了五盤菜的時候,我不想讓他炒了,那就得把他停止掉,該如何停止掉呢?

可以使用interrupt對當前線程終止,但是這個方法並不會立即終止該線程,所以我們使用Thread.currentThread().isInterrupted()判斷是否有中斷信號,以此來停止當前線程,這樣程序就會break跳出循環,程序的不執行也就是真正意義上的線程終止

<code>/<code>

那麼再來一個問題,如果我想讓煮米的人發現炒菜的人炒了10盤菜後,告訴炒菜的人停止炒菜該怎麼做呢?


分享到:


相關文章: