Java 中Thread用法

1、使用線程主要有以下原因:1)解決生成進程導致的性能問題;2)用於同時處理;3)合理利用CPU資源。

2、Java 線程的運行:構造一個Thread類的實例(主要有兩種方法),調用其start()方法,如:

Thread t = new Thread();
t.start();
  • 1
  • 2
  • 1
  • 2

這是一個空殼線程,不做任何事,創建之後就退出。 
構造一個Thread類的實例的兩種方法:1)
派生Thread的子類,覆蓋run()方法;2)實現一個Runnable接口,將Runnable對象傳遞給Thread構造函數。採用方法2)更能區分線程和線程執行的任務。

3、(派生,extends)Thread類有多個方法,採用派生Thread並覆蓋run()方法時,不能覆蓋其他方法,如start(),stop(),interrupt(),join(),sleep()等等。簡單示例:

public class MyThread extends Thread {
    private String msg;
    public MyThread(String s) {
        msg = s;
    }
    public void run() {
        System.out.println(msg);
    }
}

public static void main(String[] args) {
    String s = "Hello world.";
    MyThread t = new MyThread(s);
    t.start();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

當然這個示例也沒有實際應用,因爲沒有利用多線程完成I/O和CPU之間的平衡問題。僅作爲示例

4、(實現接口,implements)爲避免覆蓋標準Thread中的其他方法,可以將任務編寫爲Runnable的一個實例。將上述代碼修改一下:

public class MyRunnable implements Runnable {
    private String msg;
    public MyRunnable(String s) {
        msg = s;
    }
    public void run() {
        System.out.println(msg);
    }
}

public static void main(String[] args) {
    String s = "Hello world.";
    MyRunnable mr = new MyRunnable(s);
    Thread t = new Thread(mr);
    t.start();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

這樣就能創建線程了。 
那線程之間如何交互?

5、Thread類中run()和start()方法既不接收參數也不返回結果。線程接收參數容易實現,不管是派生子類還是實現Runnable接口,都可以在構造函數中將參數傳遞進去;那線程運行的結構如何返回?

不好的方法——1):派生子類中實現一個public type[] getResult()的方法,這樣在線程運行結束的時候得到結果。問題在於不知線程結束時間,當調用getResult()的時候,也許run()還沒有運行完。 
不太好的方法——2):輪詢,既當run()方法調用結束後,修改子類某字段或者是有個標誌。時間效率太差。

比較好的方法——3):回調。要想獲得線程運行之後的結果,不一定去請求線程的方法,也可以用線程去通知主程序。回調也有兩種方法,一是靜態方法,二是回調實例方法。靜態方法雖然簡單,但是不夠靈活。以下假如主程序通過調用其它線程分析字符串中的計算式子,得到結果,並打印。 
調用靜態方法的僞代碼:

public class CallBack implements Runnable {
    private String source;
    private String result;
    public CallBack(String s) {
        this.source = s;
    }
    public void run() {
        try{
            ...
            result = ...;
            CallBackUserInterface.receiveResult(source, result);
        }
        catch(Exception e) {
        ...
        }
    }
}

public class CallBackUserInterface {
    public static void receiveResult(String source, String result) {
        Sytem.out.println(source + " = " +  result);
    }
    public static void main(String[] args) {
        for(int i = 0; i < args.length; i++) {
            CallBack cb = new CallBack(args[i]);
            Thread t = new Thread(cb);
            t.start();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

而調用實例方法的僞代碼:僅列出改變的地方

public class InstanceCallBack implenments Runnable {
    private String source;
    private String result;
    private InstanceCallBackUserInterface callBack;
    public CallBack(String s, InstanceCallBackUserInterface callBack) {
        this.source = s;
        this.callBack = callBack;
    }
    public void run() {
        try{
            ...
            result = ...;
            //Here
            callBack.receiveResult(result);
        }
        catch(Exception e) {
        ...
        }
    }
}

public class InstanceCallBackUserInterface {
    private String source;
    private String result;
    public InstanceCallBackUserInterface(String source) {
        this.source = source;
    }
    public void calculate() {
        InstanceCallBack cb = new InstanceCallBack(souece, this);
        Thread t = new Thread(cb);
        t.start();
    //Here
    void receiveResult(String result) {
        this.result = result;
    }
    public String toString() {
        String s = source + " = " + result;
        return s;
    }
    public static void main(String[] args) {
        for(int i = 0; i < args.length; i++) {
            InstanceCallBackUserInterface c = new InstanceCallBackUserInterface(args[i]);
            c.calculate();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

這裏沒有將calculate()方法放在構造方法裏,主要是擔心構造方法未完成calculate裏的線程便執行結束。 
回調實例函數雖然有點複雜,但是多個類都關心線程運行結果時,回調靜態函數就無能爲力了,而如果多個類都實現了一個統一的接口用於接收結果,則可以向多個對象發送運行結果。

public interface ResultListener {
    public void receiveResult(String result);
}

public class ListCallBack implements Runnable {
    List listenerList;
    private String source;
    private String result;
    public synchronized void addListener(ResultListener l) {
        listenerList.add(l);
    }
    public synchronized void removeListener(ResultListener l) {
        listenerList.remove(l);
    }
    public synchronized sendResult() {
        ListIterator iterator = listenerList.listIterator();
        while(iterator.hasNext()) {
            ResultListener rl = (ResultListener)iterator.next();
            rl.receiveResult(result);
        }
    }
    public void run() {
        ...
        sendResult();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

調用者類先構造一個ListCallBack,並將自己添加到listenerList中,再運行線程。

6、同步問題。由synchronized關鍵詞修飾,可修飾對象或者方法。 
而同步容易導致死鎖,所以同步儘量少使用。同步使用的情景一般有全局變量,所以減少全局變量可以使同步減少。

7、線程調度。 
線程優先級有1~10,數字大優先級高,與一般情況不同。Thread中有getPriority()和setPriority()兩個方法。 
線程調度的時機:1)阻塞;2)顯示放棄(Thread的yield()方法);3)休眠(Thread 的sleep()方法,精度依賴平臺);4)連接線程,Thread的join()方法,某線程a在其代碼中調用線程t的join()方法,其後的代碼要在線程t運行結束再運行;5)等待一個對象,調用Object的wait()方法,每個對象都有,當此對象的notify()方法調用之後,運行wait()之後的代碼,另外wait()方法也可以傳入時間參數,即等待一段時間就運行其後代碼;6)當線程結束也進行線程調度。而調用sleep,join,wait方法都有被中斷(interrupt)的可能,一旦捕獲到中斷異常,則執行catch(InterruptException ex)裏的代碼。

8、線程池。 
儘管線程與進程相比開銷小很多,但是頻繁創建與刪除依然影響性能。雖然線程停止之後不能重啓,但是可以進行改造。首先保存一個全局的任務池並創建固定數量的線程。當池爲空時,線程等待;當向池中添加一個任務時,所有等待線程得到通知。當一個線程結束其分配的任務時,再去從池中獲取新的任務。 
需要注意的是,如何告知程序已經結束。一般需要再設置兩個全局變量,一個是總任務數,一個是已經完成的任務數。

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