Java多線程(4)——線程通信

線程之間的關係是平等的,彼此之間並不存在任何依賴,它們各自競爭CPU資源,互不相讓,並且還無條件地阻止其他線程對共享資源的異步訪問。然而,也有很多現實問題要求不僅要同步的訪問同一共享資源,而且線程間還彼此牽制,通過相互通信來向前推進。那麼,多個線程之間是如何進行通信的呢?

參考資料:Java程序中的多線程http://www.ibm.com/developerworks/cn/java/multithreading/

爲什麼會排隊等待?

下面的這個簡單的 Java 程序完成四項不相關的任務。這樣的程序有單個控制線程,控制在這四個任務之間線性地移動。此外,因爲所需的資源 ― 打印機、磁盤、數據庫和顯示屏 -- 由於硬件和軟件的限制都有內在的潛伏時間,所以每項任務都包含明顯的等待時間。因此,程序在訪問數據庫之前必須等待打印機完成打印文件的任務,等等。如果您正在等待程序的完成,則這是對計算資源和您的時間的一種拙劣使用。改進此程序的一種方法是使它成爲多線程的。

class myclass {
static public void main(String args[]) {
    print_a_file();
    manipulate_another_file();
    access_database();
    draw_picture_on_screen();
    }
}

線程也稱爲輕型進程 (LWP)。因爲線程只能在單個進程的作用域內活動,所以創建線程比創建進程要廉價得多。這樣,因爲線程允許協作和數據交換,並且在計算資源方面非常廉價,所以線程比進程更可取。(線程需要操作系統的支持,因此不是所有的機器都提供線程。)

線程組
線程是被個別創建的,但可以將它們歸類到 線程組中,以便於調試和監視。只能在創建線程的同時將它與一個線程組相關聯。在使用大量線程的程序中,使用線程組組織線程可能很有幫助。可以將它們看作是計算機上的目錄和文件結構。

線程間通信
當線程在繼續執行前需要等待一個條件時,僅有 synchronized 關鍵字是不夠的。雖然 synchronized 關鍵字阻止併發更新一個對象,但它沒有實現線程間通信 。Object 類爲此提供了三個函數:wait()、notify() 和 notifyAll()。以全球氣候預測程序爲例。這些程序通過將地球分爲許多單元,在每個循環中,每個單元的計算都是隔離進行的,直到這些值趨於穩定,然後相鄰單元之間就會交換一些數據。所以,從本質上講,在每個循環中各個線程都必須等待所有線程完成各自的任務以後才能進入下一個循環。

wait():讓線程處於凍結狀態,被wait的線程會被存儲到線程池中;

notify():喚醒線程池中的任意一個線程;

notifyAll():喚醒線程池中的所有線程。

這些方法都必須定義在同步中。


下面是一個簡單的線程間通信案例:輸入/輸出的通信

class Resource{
	 String name;
	 String sex;
	boolean flag = false;
}

class Input implements Runnable{
	Resource r;
	Input(Resource r){
		this.r = r;
	}
	//這裏的synchronized同步代碼塊可以封裝到Resource類中作爲一個synchronized函數
	public void run(){
		int x=0;
		while(true){
			synchronized(r){
				if(r.flag){
					try{
						r.wait();
					}catch(InterruptedException e){
						//e.printStack();
						}
				}
				if(x==0){
					r.name="woca";
					r.sex="male";
				}else{
					r.name="wori";
					r.sex="female";
				}
				
				r.flag = true;
				r.notify();
			}
			x = (x+1)%2;
		}
	}
}

class Output implements Runnable{
		Resource r;
		Output(Resource r){
				this.r = r;
			}
		public void run(){
				while(true){
						synchronized(r){
								if(!r.flag){
										try{
												r.wait();
											}catch(InterruptedException e){
													//e.printStack();
												}
									}
								System.out.println(r.name+"...."+r.sex);
								r.flag = false;
								r.notify();
							}
					}
			}
}

class ResourceDemo{
		public static void main(String[] args){
				Resource r = new Resource();
				Input in = new Input(r);
				Output out = new Output(r);
				Thread t1 = new Thread(in);
				Thread t2 = new Thread(out);
				t1.start();
				t2.start();
			}
	}


wait 和 sleep 區別?

1.wait可以指定時間也可以不指定。

   sleep必須指定時間。

2.在同步中時,對cpu的執行權和鎖的處理不同。

wait:釋放執行權,釋放鎖。
sleep:釋放執行權,不釋放鎖。


調試大量的線程,需要我們對每個線程進行狀態判斷

public class Probe extends Thread {
    public Probe() {}
    public void run() {
        while(true) {
            Thread[] x = new Thread[100];
            Thread.enumerate(x);
            for(int i=0; i<100; i++) {
            Thread t = x[i];
            if(t == null)
                break;
            else
                System.out.println(t.getName() + "\t" + t.getPriority()
                + "\t" + t.isAlive() + "\t" + t.isDaemon());
            }
        }
    }
}


限制線程優先級和調度

Java 線程模型涉及可以動態更改的線程優先級。本質上,線程的優先級是從 1 到 10 之間的一個數字,數字越大表明任務越緊急。JVM 標準首先調用優先級較高的線程,然後才調用優先級較低的線程。但是,該標準對具有相同優先級的線程的處理是隨機的。如何處理這些線程取決於基層的操作系統策略。在某些情況下,優先級相同的線程分時運行;在另一些情況下,線程將一直運行到結束。請記住,Java 支持 10 個優先級,基層操作系統支持的優先級可能要少得多,這樣會造成一些混亂。因此,只能將優先級作爲一種很粗略的工具使用。最後的控制可以通過明智地使用 yield() 函數來完成。通常情況下,請不要依靠線程優先級來控制線程的狀態。







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