1. 程式人生 > >深入理解併發程式設計 -- 多執行緒(一)

深入理解併發程式設計 -- 多執行緒(一)

併發程式設計 -- 多執行緒(一)

作者 : Stanley 羅昊

【轉載請註明出處和署名,謝謝!】

程序

在理解多執行緒之前,我們先需要了解什麼是程序?

程序說白了就是在你的記憶體空間中開闢出的一個獨立的空間;

如果還不理解的話,我再解釋一下;

想必各位之前都安裝過軟體吧,你安裝完軟體之後,在你的軟體安裝包裡面是不是有一個.exe檔案,那你雙擊exe檔案的時候,在你的工作管理員,在裡面就有一個程序選項卡,就是說,每當你開啟一個exe檔案的時候,它都會顯示在工作管理員的程序當中,所以就可以把執行中的任意一款軟體,都可以把它看做一個程序;

當然,以上的操作方式是在windows系統的操作的,也就是說,想檢視Windows的程序,只需要在工作管理員中檢視即可;

在linux下使用命令 ps 或 pstree、ps -eflgrep;

如果想在linux系統下檢視java的相關程序,命令為:jps;

那麼問題就來了,當你開啟QQ的時候,是不是就是開啟了一個程序,當你開始使用它並且聊天的時候,比如你是a,你現在要跟b聊天然後再去跟c聊天,那麼這樣的操作是不是相互獨立的呢?也就是說,你現在要跟b傳送你的遊戲密碼,這個時候c問你晚上吃啥飯,你發的密碼c知道嗎?肯定不會啦,所以你跟b聊天的時候,是不會影響你跟c聊天的,因為你跟b c 是相互獨立的;

那麼,在這個裡面,你跟他們每個人產生的通話底層是怎麼實現的呢?

底層就是靠執行緒去實現的;

執行緒

什麼是執行緒呢?

執行緒是指程式在執行過程中,能夠執行程式程式碼的一個執行單元,在Java語言中,執行緒有四種狀態:執行,就緒,掛起,結束。一般情況下,一個作業系統是有多個程序,那麼每個程序都要對應多個執行緒;

執行緒與程序有區別嗎?

有,程序是一段正在執行的程式,而執行緒有時也被稱為輕量級程序,它是程序的執行單元,一個程序可以擁有多個執行緒,各個執行緒之間共享程式的記憶體空間,但是,各個執行緒擁有自己的棧空間。

一個程序可以有很多執行緒,每條執行緒並行執行不同的任務;

實現單個執行緒

在之前的一些簡單的java練習中,我們執行的時候,是不是都是在main方法中測試執行啊,那麼,在這之前,我僅僅編寫了一些非常簡單的java程式碼,甚至就在main方法中輸出了一句話,就可以直接完成執行,在這期間,我並沒有建立有關執行緒方面的方法以及程式,那麼就怎麼實現運行了呢?

其實很簡單,Main方法既然能執行你的程式,那麼必然就會有一個執行緒,那麼這個執行緒就是單執行緒,那,我們如何檢視本次執行執行緒的執行緒名呢?

我們僅需在main方法裡面輸出以下即可:

System.out.println(Thread.currentThread());

打印出來後,我們就可以看到執行緒名師Main,因為就一個執行緒,所以main就是主執行緒;

那麼就一個執行緒,就表明,在這之前,我們所做的一些練習程式都是單執行緒的;

執行緒的建立方式

兩種:

1.繼承(extends) Thread

繼承完Thread類之後需要重寫run方法;

2.實現(implem) Runnable介面

也許需要重寫run方法;

繼承Thread類建立執行緒

講了那麼多,那麼就開始上手實操一下,我們將要練習的是繼承Thread來建立一個執行緒;

首先,我們建立一個類(MyThread)然後繼承Thread類;

繼承完Thread後,實現run方法;

run方法的作用就是,相當於這個執行緒對使用者提供的一個介面;

所以,使用者有什麼業務邏輯,都需要寫在run方法裡面:

public class MyThread extends Thread{

    public void run(){

                  //作用:相當於這個執行緒向用戶提供的介面,使用者有什麼樣的業務邏輯,寫在這個方法中

    }

}

那我們現在就讓這個run方法就實現一個簡單的列印;

這個就是我使用了第一種方式來建立了一個執行緒;

那麼,你這個執行緒建立完成後,如果你想呼叫它怎麼辦?

呼叫執行緒

我再建一個類(TestThread);

然後提供一個Main方法;

在main方法中建立一個執行緒,語法:

//建立一個執行緒

MyThread mt = new MyThred();

這個就是建立執行緒,建立完後,下一步我們需要呼叫start方法;

public class TestThread{

public static void main (String [] args){

//建立執行緒

MyThread mt = new MyThred();

//啟動執行緒

mt.start();

       }

   }

為什麼要呼叫start,而不是run?

其實很簡單,mt.start就是啟動你的執行緒,那麼啟動後,它底層就會去呼叫你的run方法;

這個就是執行緒的啟動式start方法;

我們執行一下後,看一下控制檯列印:

就是一些輸出,感覺就跟呼叫方法一樣,對吧;

那麼,接下來,我們看一下第二種建立方式

實現(implem) Runnable介面建立執行緒

這種方法跟上面的那種,大同小異,只不過上面那個是繼承,這個是實現介面;

實現Runnable介面後,需要實現它的run方法;

public class MyRunnableThread implements Runable{

    public void run(){

                  //作用:相當於這個執行緒向用戶提供的介面,使用者有什麼樣的業務邏輯,寫在這個方法中

    }

}

現在,我們用第二種方式建立了一個執行緒,當然,業務邏輯跟上面的那個相同,因為舉例,所以沒有深究別的;

第二種方式呼叫執行緒就有所不同

 因為執行執行緒永遠需要Thread裡面的start方法來啟動執行緒,所以需要把Thread創建出來,再將創建出來的執行緒放進去;

所以打印出來的結果是跟上面的結果是一樣的,這裡就不再放圖上去了;

執行緒關鍵字分析

start,是執行緒啟動的方法;

run方法是執行緒執行過程中呼叫的方法(預設呼叫),在上面的例子我們也看到了,你並沒有手動去呼叫run方法,是他自動呼叫的,就跟你建立物件的時候,預設呼叫構造方法一樣;

深究run與start

那,啟動執行緒一定是要用staet方法啟動,我試試不用它,我直接呼叫Thread中的run方法可行嗎?

可行,因為拋開執行緒,你本身就是例項化了Thread這個類,並呼叫該類中的run方法是沒有問題的,但是,不納入執行緒中!!

我們直接呼叫run方法後,發現,方法可以正常列印,因為,僅僅完成了普通方法的呼叫,實際上並沒有啟動執行緒;

&n