Java多線程學習總結

在開始這個話題之前,我們先聊一下線程。線程的目的就是爲了讓一些程序片段能夠“同時”進行,至少是視覺上。因爲實際上,這些程序片段是“交替”執行的。如圖1

在途中,線程A和線程B輪流交替地佔有CPU的處理時間。也許有人說,CPU總的計算量並沒有降低啊,確實如此,還可以切換線程,還增加了一點點的計算量。如果現在有一個很長很複雜很費時的計算任務,和一個精彩電影的播放任務,那我們當然願意選擇讓這兩個不太相干的任務同時併發執行。由此可見線程的目的是用一些調度算法,使電腦用戶不會“等待”的不耐煩。因爲絕大多數的用戶都希望計算機體貼周到,富有人性化,提供更好的服務。事實上,我們在計算機上做的很多革新,就是爲了如此。

       Java是支持線程的,不像C++,還需要操作系統的配合才行,當然java最終還是要用到操作系統底層的支持,纔可以擁有多線程編程的能力,呵呵。Java實現線程非常簡單。一個是繼承Thread,一個是實現Runnable接口。我突然想起我畢業時面試時經常被問這樣的問題,那時只不過是機械式的回答,裏面有許多的道理倒沒有現在認識深刻。我們需要併發的代碼寫在run方法裏面就OK了,呵呵。至於java底層是如何壓棧入棧,保護程序執行現場的,如何恢復現場的,我們就不必深究了,因爲我們不是API級的程序員,我們需要的是學習別人的成果,迅速copy,快速開發,不然怎麼超英趕美啊?我們的目的是爲了圖2中的“順序執行”變成圖1中的“併發執行”。其中圖1中的“時間片”的劃分是隨機的。

 

Java的線程執行,也是非常簡單的,new 一個線程,然後startOK了。

關於Thread這個類,有時候容易混淆。打個比方說,使用這個類可以類比成英語裏面的語法“Somebody do Something”。舉個例子:

SomeBody類:

  

package wjj2;

public class SomeBody {
   
    private String name;
    public SomeBody(String name)
    {
        this.name=name;
    }
   
    public void someBodyDo()
    {
        System.out.println(this.name+"早上7:45起牀");
        //隨機生成停頓時間,爲了模擬較長的代碼執行時間,以下都是類似
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
        System.out.println(this.name+"7:50大便");
       
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
        System.out.println(this.name+"8:00刷牙,順帶刷刷舌苔");
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
        System.out.println(this.name+"8:05洗臉");
       
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(this.name+"8:10搽雪花膏");
       
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(this.name+"8:20到車站");
       
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(this.name+"8:30搭上車");
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
        System.out.println(this.name+"8:40到公司吃飯");
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
        System.out.println(this.name+"8:50打開電腦發呆");
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(this.name+"9:00開始工作");
    }

}

   

 

具有“打亂”代碼執行順序能力的Thead子類:SomeBodyWork類:

package wjj2;



public class SomeBodyWork extends Thread{


    private SomeBody person;


    public SomeBodyWork (SomeBody person)


    {


        this.person=person;


    }



    public void run()


    {


        person.someBodyDo();


    }



}


 

 

 

程序入口類:main類:

package wjj2;

public class main {
    public static void main(String args[])
    {
        SomeBody xiaoming=new SomeBody("小明 ");
        SomeBodyWork thread1=new SomeBodyWork(xiaoming);
        thread1.start();
        SomeBody  dagang=new SomeBody("大剛 ");
        SomeBodyWork thread2=new SomeBodyWork(dagang);
        thread2.start();
    }

}

 

程序執行結果如下:

 

 

小明 早上7:45起牀
571
大剛 早上7:45起牀
908
小明 7:50大便
395
大剛 7:50大便
583
小明 8:00刷牙,順帶刷刷舌苔
509
小明 8:05洗臉
25
大剛 8:00刷牙,順帶刷刷舌苔
224
小明 8:10搽雪花膏
962
大剛 8:05洗臉
292
大剛 8:10搽雪花膏
665
小明 8:20到車站
554
大剛 8:20到車站
278
大剛 8:30搭上車
934
小明 8:30搭上車
577
小明 8:40到公司吃飯
238
小明 8:50打開電腦發呆
812
大剛 8:40到公司吃飯
453
大剛 8:50打開電腦發呆
361
小明 9:00開始工作
大剛 9:00開始工作

 

 

通常Somebody自成一個單獨的類,線程的目的就是:讓 Somebody 併發地 doSomething

從程序的結果來看:

 1.兩個線程的代碼確實像圖1一樣,交替執行,且交替順序是隨機的,多試幾次會發現執行結果會有不同。

 2.線程的run()方法提供了一種機制,一種把代碼執行順序“打亂”的機制,有些初學者喜歡在這個提供這個機制的類中,寫一下“實體”類的方法,比如,他會讓SomeBody類在SomeBodyWork中做一些事情,如

 

        System.out.println(this.name+"早上10:00開始編碼");
        System.out.println(this.name+"早上11:00開始測試");
        System.out.println(this.name+"下午14:45開始修改代碼");

 

 

 雖然Java中,什麼東西都是對象,什麼東西都以對象的模板“類”來展現,但類和類之間,還是有區別的,

很顯然,以上3行代碼,還是屬於“實體”這些類的方法,應該移植在“實體”類中,Thread這個類,提供了一種機制,這種機制用“類”來展現,很神奇吧,呵呵。所以最好不要把雜七雜八的東東放在Thread的子類中,就是一個run()最好,Thread就是純粹用來“併發”地run的。關於代碼的歸類,可以參考《重構--改善既有代碼的設計》,Martin Fowler著,侯捷,熊節譯,中國電力出版社。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章