1. 程式人生 > >Java多執行緒程式設計總結筆記——01 Java語言的執行緒

Java多執行緒程式設計總結筆記——01 Java語言的執行緒

GUI應用程式

幾乎所有的GUI應用程式都會用多執行緒。舉例來說加入現在有人在用word編輯一個比較大的文字檔案剛剛才做過單字“查詢”操作,當word進行查詢時,螢幕上會出現“停止查詢按鈕”,使用者可以隨時停止查詢。這個功能其實就用到了多執行緒。

(1)執行查詢

(2)顯示按鈕,若按鈕按下則停止查詢

這兩個操作分別交給不同的執行緒進行。這樣一來執行執行緒(1)的執行緒可以專心查詢,執行(2)的執行緒也可專心在GUI操作上,程式就會變得比較簡單。

比較花費時間的I/O操作

多個客戶端

基本上網路上的伺服器必須同時處理一個以上的客戶端,不過,一定要在伺服器這邊的程式設計加入一個以上客戶端的概念的話,程式會變得更復雜。此時,不妨準備一個當有客戶端連線到伺服器的時候,會自動出來迎接這個客戶點的執行緒,這樣一來,伺服器的程式就可以設計成好像只服務一個客戶端

併發與並行

當有一個以上的執行緒的執行緒來操作時,若計算機只有一箇中央處理器,根本不可能進行一個以上的處理。

如果在有一個以上的中央處理器的計算機上跑程式,則執行緒的程式可能是並行(parallel)而非併發,就可以同時執行一個以上的處理。

執行緒的啟動

/**
 * @Title: Thread1.java
 * @Package: org.lenvon.thread
 * @Description: TODO
 * @author: Lenvon
 * @date: 2015年6月17日 下午3:06:51
 * @version: V1.0
 */
package org.lenvon.thread;

/**
 * @moudle: Thread1
 * @version:v1.0
 * @Description: TODO
 * @author: Lenvon
 * @date: 2015年6月17日 下午3:06:51
 * 
 */
public class Thread01 {

    static class PrintThread extends Thread {
        private String msg ;

        /**
        * <p>Title: </p>
        * <p>Description: </p>
        * @param msg
        */ 
        public PrintThread(String msg) {
            this.msg = msg;
        }

        /**
        *
        * <p>Title: run</p>
        * <p>author : Lenvon</p>
        * <p>date : 2015年6月17日 下午3:08:40</p>
        */ 
        @Override
        public void run() {
            // TODO Auto-generated method stub
            System.out.println(msg);
        } 

    }

    /**
     * 
     * <p>Title: main</p>
     * <p>author : Lenvon</p>
     * <p>date : 2015年6月17日 下午3:06:51</p>
     * 
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new PrintThread("Good").start();
        new PrintThread("Nice").start();
        System.out.println("End---------------");
    }

}

“PrintThread的例項”和“執行緒本身”是兩個不同的部分,即使建立了“PrintThread的例項”,也還沒有啟動執行緒,而且就算執行緒已經結束,PrintThread的例項也不會就這樣消失不見。別忘了,在字串輸出結束之前,已經啟動的兩個執行緒還活著,一直要等到所有執行緒都已經結束,程式才會結束。當所有執行緒都結束,這個程式才會正式結束。

Thread類和Runnable介面

Thread類也實現了Runnable 介面,也有run()方法,只不過Thread類的run()方法的主體是空的沒有執行任何部分。Thread類的run()方法通常是被子類覆蓋(override)

Synchronized例項方法和Synchronized阻擋

假設現在有一個型別如下的synchronized例項方法

synchronized void method(){

    …

}

在功能上和下面以synchronized阻擋為主的方法有異曲同工之妙。

void method(){

   Synchronized(this){

        …

   }

}

換句話說synchronized方法是使用this鎖去做執行緒的共享互斥。

Synchronized類方法和Synchronized阻擋

假設現在有一個型別如下的synchronized的類方法,synchronized類方法有限制同時只能讓一個執行緒執行。這部分和synchronized例項方法一樣,但是兩者是有不同的。

Class Something{

    static synchronized voidmethod(){

           …

     }

}

在功能上和下面以synchronized阻擋為主的方法有異曲同工之妙。

Class Something{

    static void method(){

       synchronized(Something.class){

             …

         }

    }

}

換句話說synchronized的類方法是使用該類的類物件的鎖去做執行緒的共享互斥。Something.class是對應Something類的java.lang.Class類的例項。

Wait方法——把執行緒放入wait set

使用wait方法時,執行緒便進入wait set,假設現在已經執行如下語句:obj.wait();

則目前的執行緒停止執行,進入例項obj的的wait set.這個操作成為:執行緒在obj上wait.

如果例項方法還有如下的語句時:wait();

則其意義同:this.wait();

故執行wait的執行緒就會進入this的wait set.此時就變成了在this上wait.

如欲執行wait()方法,執行緒需獲取鎖定(這是規則)。但是當執行緒進入wait set時,已經釋放了該例項的鎖定。

Notify方法——從wait set拿出執行緒

使用notify()(通知)方法時,可以從wait set拿出一個執行緒。

obj.nitify();則從wait set裡的執行緒中挑出一個,喚醒這個執行緒。被喚醒的執行緒便退wait set

Notify後的執行緒

被notify喚醒的執行緒不是在notify後立即執行,因為在notify的那一刻,執行notify 的執行緒還握著鎖定不放,所以其他執行緒無法獲取該例項的鎖定。

Notify如何選擇執行緒

假設執行notify方法時,wait set裡面正在執行的執行緒不止一個。規格並沒有註明此時該選擇哪一個執行緒。究竟是選擇等待執行緒裡面的第一個,隨機選擇或是另以其他方式選擇,則以java處理系統而異。所以在寫程式時,程式屬性最好不要寫成會因所選執行緒而有所變動。

notifyAll()方法——從wait set 拿出所有執行緒

使用notifyAll(通知全體)方法時,會將所有在waitset苦等的執行緒都拿出來。

obj.notifyAll()則會喚醒所有留在例項obj的wait set裡的執行緒。

而notifyAll();則其意義同this.notifyAll();故這個語句所在方法的例項(this)的wait set裡的執行緒會全部放出來。

跟wait方法和notify方法一樣,執行緒必須要獲取要呼叫例項的鎖定,才能呼叫notifyAll方法。

被喚醒的執行緒便開始去獲取剛才wait時釋放掉的鎖定,那麼現在這個鎖定現在是在誰的手中呢?沒錯,鎖定就是在剛才執行notifyAll方法的程式手裡,因此即使所有執行緒都退出了wait set,但他們仍然在去獲得鎖定的狀態下,還是有阻擋。要等到剛才執行notifyAll方法的執行緒釋放出鎖定後,其中一名幸運兒才會實際執行。

要是沒有鎖定呢

若沒有鎖定的執行緒去呼叫wait,notify或notifyAll時,便會丟擲異常java.lang.IllegalMonitorStateException.

呼叫notify方法還是notifyAll方法

Notify方法和notifyAll方法兩者非常相似,到底該用哪一個?老實說,這個選擇有點難。選擇notify的話,因為要喚醒的執行緒比較少,程式處理速度當然要比notifyAll略勝一籌。但是選擇notify時,若這部分程式處理的不好,可能會有程式掛掉的危險性,一般說來,選擇notifyAll所寫出來的程式程式碼要比選擇notify可靠。除非你能確定程式設計師對程式程式碼的意義和能力限度一清二楚,否則選擇notifyAll應該比較穩紮穩打