【java編程開發】線程詳解及源碼(史上最經典的線程詳解)

線程的概念:

程序、進程、線程、多 線程

程序(Program)是爲完成特定任務、用某種語言編寫的一組指令的集合,指一段靜態的代碼。作爲一個靜態文件存儲在計算機系統的硬盤等存儲空間中

進程(Process)是正在被操作系統運行的應用程序

線程(Thread)是指進程中的一個執行流程。一個進程可以由多 個線程組成,即在一個進程中可以同時運行多個不同的線程,這些線程共享進程的內存空間,分別執行不同的任務。

多線程的概念:

多 線程指在單個程序中可以同時運行多 個不同的線程,執行不同的任務,這是實現併發機制的一種有效手段

操作系統使用分時管理各個進程,按CPU時間片輪流執行每個進程;Java的多  

public class My1stThreadExtendsThread extends Thread {
    public static void main(String[] args) {
        // 獲取當前線程對象,這裏是主線程對象
        Thread t = Thread.currentThread();
        out.println("主線程是:" + t);
        // 創建子線程並啓動
        My1stThreadExtendsThread mt = new       					My1stThreadExtendsThread();
        mt.start();
    }
    // 重寫父類的run()方法
    public void run() {
        // 獲取當前線程對象,這裏是子線程對象
        System.out.println("子線程是:" + Thread.currentThread());
        // 完成遊戲的倒計時功能
        …………
    }
}

線程就是在操作系統每次分時給Java程序一個時間片的CPU時間內,在若干獨立的可控制的線程之間切換

多 線程的目的是爲了最大限度的利用CPU資源

主線程:

當Java程序啓動時,一個線程立刻運行,該線程通常叫做程序的主線程(main thread)。每個Java程序都有一個隱含的主線程。
主線程的重要性
A、它是產生其他子線程的線程。
B、一般來講,主線程最先啓動且最後完成執行(因爲很多時候主線程需要完成各種關閉動作),但並不總是這樣的

儘管主線程在程序啓動時自動創建,但它可以由一個Thread對象控制

import static java.lang.System.out;//靜態導入
public class My1stMainThread extends Thread {
	public static void main(String[] args) {
		Thread t = Thread.currentThread();//獲取當前線程對象,這裏是主線程
		out.println("當前線程是:" + t);
		t.setName("主線程");// 改變主線程內部名稱
		out.println("當前線程是:" + t);
		try {
			for (int i = 0; i < 5; i++) {
				out.println(i);
				Thread.sleep(1000);// 線程睡眠一秒鐘
			}
		} catch (InterruptedException ex) {
			out.println("主線程中斷!");
			ex.printStackTrace();
		}
	}
}

線程的創建和啓動:
創建線程有兩種方式: 
A、定義一個Thread類的子類,覆蓋Thread類的run()方法,然後創建該子類的實例。
B、定義一個實現Runnable接口的類,實現它的run()方法,然後將這個類的實例作爲Thread類構造方法的參數,創建Thread類的實例


調用start()方法啓動線程,不要直接調用run方法,直接調用run方法僅僅是將run作爲一個普通的實例方法來調用而不會啓動一個線程
{通過擴展Thread類來創建線程}

public class My1stThreadExtendsThread extends Thread {
    public static void main(String[] args) {
        // 獲取當前線程對象,這裏是主線程對象
        Thread t = Thread.currentThread();
        out.println("主線程是:" + t);
        // 創建子線程並啓動
        My1stThreadExtendsThread mt = new       					My1stThreadExtendsThread();
        mt.start();
    }
    // 重寫父類的run()方法
    public void run() {
        // 獲取當前線程對象,這裏是子線程對象
        System.out.println("子線程是:" + Thread.currentThread());
        // 完成遊戲的倒計時功能
        …………
    }
}


通過實現Runnable接口來創建線程
public class My1stThreadImplRunnable implements Runnable {
    public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();// 獲取主線程對象
        out.println("主線程是:" + mainThread);
        My1stThreadImplRunnable mt = new 								My1stThreadImplRunnable();
        Thread t1 = new Thread(mt);// 創建一個子線程對象
        t1.start();// 啓動子線程
    }
//重寫父類的run()方法
    public void run() {
        out.println("子線程是:" + Thread.currentThread());
        // 完成遊戲的倒計時功能
         …………
    }
}

建線程兩種方法的區別:

區別:Java中通過實現Runnable接口創建線程的方法更常用,因爲Java不支持多繼承,有些類需要繼承其他類來完成功能,無法再繼承Thread類完成多線程功能,此時必須通過實現一個接口來增加多線程功能。

相同點:都要通過Thread類才能完成線程的創建



線程啓動:

儘管線程執行的是 run()方法中的代碼,但實際上是通過 start()方法觸發的,而不是直接調用run方法


用start方法來啓動線程,真正實現了多線程運行


 run()方法只是類的一個普通方法而已,如果直接調用run()方法,程序中依然只有主線程這一個線程


總結:調用start()方法方可啓動線程,而run()方法只是thread的一個普通方法調用


線程的生命週期:


同進程一樣,線程也有從創建、運行到消亡的過程,這稱爲線程的生命週期。

創建狀態
  用new關鍵字和Thread類或其子類建立一個線程對象後,該線程對象就處於創建狀態,這是線程已被創建但未開始執行的特殊狀態。處於創建狀態的線程是一個空的線程對象,系統不爲它分配資源,但有自己的內存空間。創建狀態的線程通過調用start()方法進入就緒狀態。




就緒狀態
處於就緒狀態的線程已經具備了運行條件,但還沒有分配到CPU,因而將進入線程隊列,等待系統爲其分配CPU。
線程根據自身優先級進入等待隊列的相應位置。
一旦獲得CPU,線程就進入運行狀態,並自動調用自己的run()方法。
運行狀態
進入運行狀態的線程順序執行自己run()方法中的代碼,直到調用其他方法而終止,或等待某資源而阻塞,或完成任務而死亡。
運行狀態表示線程擁有了對處理器的控制權,其代碼正在運行,除非運行過程的控制權被另一優先級更高的線程搶佔,否則這個線程將一直持續到運行完畢




阻塞狀態
處於運行狀態的線程在某些情況下讓出CPU並暫時終止自己的運行,進入阻塞狀態。
A、執行了sleep()方法
B、執行了wait()方法
C、等待I/O設備等資源
在阻塞狀態的線程不能進入就緒隊列,只有當引起阻塞的原因消除時才能轉入就緒狀態。
A、睡眠時間已到
B、其它線程通知阻塞線程
C、等待的I/O設備空閒
當再次獲得CPU時,從原來終止的位置開始繼續運行。




死亡狀態
這是線程生命週期中的最後一個階段,表示線程已退出運行狀態,並且不再進入就緒隊伍。當線程的run()方法結束或由於其他原因被終止後,線程就進入消亡狀態。線程的終止分兩種情況:
A、自然死亡,即從線程的run()方法正常退出;
B、使用Thread類中的destroy()方法或stop()方法強行終止
注意:Java系統不贊成使用destroy()方法或stop()方法終止線程,因爲若使用不當,可能會導致程序的死鎖。


線程優先級:

Java中的線程優先級是在Thread類中定義的常量
NORM_PRIORITY:值爲5
MAX_PRIORITY:值爲10
MIN_PRIORITY:值爲1

缺省優先級爲:NORM_PRIORITY

有關優先級的方法有兩個:
final void setPriority(int newp):修改線程的當前優先級
final int getPriority():返回線程的優先級



class NewThread extends Thread {
	private long count = 0; //計數器,統計線程執行的次數
	private boolean isPass = true; //定義一個標誌,用來終止循環
	NewThread(String name) {
		super(name);
	}
	public void run() {
		while (isPass) { //isPass爲假時將中止循環,否則count不斷的加1
			count++;
		}
	}
	public long result() { //返回count的值
		return count;
	}
	public void stopThread() { //中止線程
		isPass = false;
	}
}


public class MultiThreadDemo4 {
	public static void main(String[] args) {
		NewThread t1 = new NewThread("Thread 1");
		NewThread t2 = new NewThread("Thread 2");
		t1.setPriority(Thread.NORM_PRIORITY + 1); // 設置優先級爲6
		t2.setPriority(Thread.NORM_PRIORITY + 3); // 設置優先級爲8
		t1.start(); // 啓動線程t1
		t2.start(); // 啓動線程t2
		try {
			Thread.sleep(500); // 主線程睡眠500毫秒
		} catch (InterruptedException e) {
			System.out.println(e.getMessage());
		}
		System.out.println("Thread 1:Priority is " + t1.getPriority()
				+ " Result of Count is: " + t1.result());
		System.out.println("Thread 2:Priority is " + t2.getPriority()
				+ " Result of Count is: " + t2.result());
		t1.setPriority(Thread.MAX_PRIORITY); // 重新設置t1的優先級爲最大
		try {
			Thread.sleep(500); // 主線程睡眠500毫秒
		} catch (InterruptedException e) {
			System.out.println(e.getMessage());
		}
		t1.stopThread();//中止線程t1
		t2.stopThread();//中止線程t2
		System.out.println("After the priority of Thread 1 is changed: ");
		System.out.println("Thread 1:Priority is " + t1.getPriority()
				+ " Result of Count is: " + t1.result());
		System.out.println("Thread 2:Priority is " + t2.getPriority()
				+ " Result of Count is: " + t2.result());
	}
}


從上面的測試可以看出優先級並不能決定線程絕對的佔用CPU,即高優先級的線程不可能一直佔用CPU的運行,低優先級的線程也不會一直閒着,實際上,線程之間的優先級並不能決定線程間本身的執行順序
Java中的線程調度採用的是搶佔式調度,即處在就緒隊列中的線程(不論優先級高還是低)一律遵循搶佔模式,當我們將一個線程的優先級設置的較高時僅僅是”建議”操作系統能夠爲它分配更多的處理時間,但這並不代表它就一定會馬上佔用CPU,因爲它仍然需要與其它線程以公平的方式進行搶佔式競爭
JVM之所以不能通過優先級來確定線程的執行順序是因爲Java的線程最終都是映射成操作系統的本地化內核線程來完成的,線程的調度由操作系統說了算,而不是JVM
我們不能編寫依賴於線程優先級的操作代碼,這樣做是危險的,針對這個問題,Java提供了更高層一級的同步化控制,這個將在後面講述到


線程的休眠

在現實問題域中,有可能需要等待一個線程執行結束後再運行另一個線程,這時可以利用Java提供的兩種方法來實現這個功能。調用線程類的isAlive() 方法和sleep() 方法來等待某個或某些線程結束。

下面這個例子將創建兩個線程,並分別依次輸出兩個直角三角形。
public class PrintThreadDemo {
	public static void main(String[] args) {
		PrintThread p = new PrintThread();//創建兩個線程
		Thread t1 = new Thread(p, "thread1");
		Thread t2 = new Thread(p, "thread2");
		t1.start();
		t2.start();
	}
}
class PrintThread implements Runnable {
	public void run() {//實現接口的run方法打印直角三角形
		System.out.println(Thread.currentThread().getName());
		for (int count = 1; count < 50; count++) {
			for (int i = 0; i < count; i++) {
				System.out.print("*");
			}
			System.out.println(); //顯示完一行以後換行輸出
		}
	}
}
編譯並運行這個程序就會發現,實際運行的結果並不一定是兩個直角三角形,有可能是一些亂七八糟的“*”號行,有的長,有的短(這取決於操作系統,Java線程在不同的操作系統下運行會有不同的結果)

Java在不進行任何控制的情況下不可能按照調用順序來執行線程,而是交替執行,主要是因爲操作系統在進行進程切換之後,其JVM進程中的各個線程會重新搶佔CPU

如果要想得到預期的結果,就需要對這兩個線程加以適當的控制,讓第一個線程先執行,並判斷第一個線程是否已經終止,如果已經終止,再調用第二個線程來執行。解決這個問題的方案是通過調用Thread類的isAlive() 方法和sleep() 方法來實現題目要求 
public class SleepThreadDemo {
	public static void main(String[] args) {
		PrintThread1 p = new PrintThread1();
		Thread t1 = new Thread(p, "thread1");
		Thread t2 = new Thread(p, "thread2");
		t1.start();//執行第一個線程
		while (t1.isAlive()) {//不斷查詢第一個線程的狀態
		   try {
			Thread.sleep(100);//讓主線程休眠100毫秒,程序不往下執行
		    } catch (InterruptedException e) {
		    }
		}
		t2.start();//直到第一個線程終止,運行第二個線程
	}
}
class PrintThread1 implements Runnable {
	public void run() {
		System.out.println(Thread.currentThread().getName());
		for (int count = 1; count < 50; count++) {
			for (int i = 0; i < count; i++) {
				System.out.print("*");
			}
			System.out.println();//顯示完一行以後換行輸出
		}
	}
}


線程的合併

Thread類中的join() 方法也可以用來等待一個線程的結束,而且這個方法更爲常用,它的語法格式如下所示:

public final void join() throws InterruptedException

該方法將使得當前線程等待調用該方法的線程結束後,再恢復執行。對前面的示例做一些改動,引入join() 方法,並觀察示例運行的結果是否有所變化。改動後的代碼如下所示。


public class JoinThreadDemo {
	public static void main(String[] args) {
		PrintThread2 p = new PrintThread2();
		Thread t1 = new Thread(p, "thread1");
		Thread t2 = new Thread(p, "thread2");
		t1.start();// 執行第一個線程
		try {
			t1.join();// 當前線程等待線程t1執行完後再繼續往下執行
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		t2.start();// 直到第一個線程終止,運行第二個線程
	}
}
class PrintThread2 implements Runnable {
	public void run() {
		System.out.println(Thread.currentThread().getName());
		for (int count = 1; count < 50; count++) {
			for (int i = 0; i < count; i++) {
				System.out.print("*");
			}
			System.out.println(); // 顯示完一行以後換行輸出
		}
	}
}


join() 方法其實很像是將while循環、isAlive() 方法以及sleep() 方法在功能上進行的組合,只不過join()方法將它們給封裝了起來。因此,使用該方法等待一個線程結束將使程序更加簡練,值得提倡。
線程的中斷

首先必須先明確“中斷”這個概念的實際含義,這裏的中斷是指一個線程在其任務完成之前被強行停止,提前消亡的過程。查閱JDK的幫助文檔,可以找到這樣一個和中斷有關的方法:interrupt()

該方法的功能是中斷一個線程的執行。但是,在實際使用當中發現,這個方法不一定能夠真地中斷一個正在運行的線程。下面通過一個例子來看一看使用interrupt() 方法中斷一個線程時所出現的結果。示例代碼如下所示:

public class InterruptThreadDemo {
	public static void main(String[] args) throws InterruptedException {
		AThread m = new AThread(); // 創建線程對象m
		System.out.println("Starting thread...");
		m.start(); // 啓動線程m
		Thread.sleep(2000); // 主線程休眠2秒,使線程m一直得到執行
		System.out.println("Interrupt thread...");
		m.interrupt(); // 調用interrupt()方法中斷線程m
		Thread.sleep(2000); // 主線程休眠2秒,觀察中斷後的結果
		System.out.println("Stopping application..."); // 主線程結束
	}

}
class AThread extends Thread {
	public void run() {
		while (true) {// 無限循環,並使線程每隔1秒輸出一次字符串
			System.out.println(getName() + " is running");
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				System.out.println(e.getMessage());
			}
		}
	}
}


通過對結果的分析,可以發現,用戶線程在調用了interrupt() 方法之後並沒有被中斷,而是繼續執行,直到人爲的終止程序爲止。這個例子說明一個事實,直接使用interrupt()方法並不能中斷一個正在運行的線程。那麼用什麼樣的方法才能中斷一個正在運行的線程呢?

下面通過在程序中引入共享變量來改進前面的示例,改進後的代碼如下所示:
public class InterruptThreadDemo2 {
	public static void main(String[] args) throws InterruptedException {
		AThread2 m = new AThread2(); // 創建線程對象m
		System.out.println("Starting thread...");
		m.start(); // 啓動線程m
		Thread.sleep(2000); // 主線程休眠2秒,使線程m一直得到執行
		System.out.println("Interrupt thread...");
		m.stop = true; // 修改共享變量
		Thread.sleep(2000); // 主線程休眠2秒,觀察中斷後的結果
		System.out.println("Stopping application..."); // 主線程結束
	}
}
class AThread2 extends Thread {
	boolean stop = false; // 引入一個布爾型的共享變量stop
	public void run() {
		while (!stop) {// 通過判斷stop變量的值來確定是否繼續執行線程體
			System.out.println(getName() + " is running");
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				System.out.println(e.getMessage());
			}
		}
	}
}



線程同步:

什麼是同步,如何實現在多線程訪問同一資源的時候保持同步呢?

首先分析一個多線程的示例,在這個示例中各線程之間共享同一數據資源,從而模擬出火車站訂票系統的處理程序,但是最後程序卻出現了意料不到的結果。這個火車站訂票系統不同步的模擬程序代碼如下

public class NoSynchronizeDemo {
	public static void main(String[] args) {
		SaleTickets m = new SaleTickets();
		Thread t1 = new Thread(m, "System 1");
		Thread t2 = new Thread(m, "System 2");
		t1.start();
		t2.start();
	}
}
class SaleTickets implements Runnable {
	private String ticketNo = "800800"; // 車票編號
	private int ticket = 1; // 共享私有成員,編號爲800800的車票數量爲1
	public void run() {
		System.out.println(Thread.currentThread().getName()
				+ " is saling Ticket " + ticketNo);//當前系統正在處理訂票業務
		if (ticket > 0) {
			try {// 休眠0-1000毫秒,用來模擬網絡延遲
				Thread.sleep((int) (Math.random() * 1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			ticket = ticket - 1; // 修改車票數據庫的信息
			// 顯示當前該車票的預訂情況
			System.out.println("ticket's amount is left: " + ticket);
		} else {
			// 顯示該車票已被預訂
			System.out.println("Sorry,Ticket " + ticketNo + " is saled");
		}
	}
}


實現同步的方法:

分析運行結果,可以發現同一時間有兩個訂票系統在處理編號爲800800的車票的預訂請求,結果由於網絡延遲的影響,該車票被預訂了兩次,使得車票的數量變成了負數。
如何解決這一問題呢?只需要在程序中引入同步機制就可以完全解決這類線程安全問題。
什麼是同步呢?當兩個或多個線程需要訪問同一資源時,它們需要以某種順序來確保該資源某一時刻只能被一個線程使用的方式稱爲同步。同步是基於“監視器”這一概念。“監視器”是用作互斥鎖的對象。監視器可被視爲類似於一個小盒子,它一次只能容納一個線程。如果一個線程進入監視器,在它退出監視器前,其他所有線程都必須等待。用這種方法,可以防止共享的資源被多個線程操縱。
可以用兩種方法同步化代碼。兩者都使用synchronized關鍵字,下面分別說明這兩種方法。

public class SynchronizeDemo {
	public static void main(String[] args) {
		SaleTickets2 m = new SaleTickets2();
		Thread t1 = new Thread(m, "System 1");
		Thread t2 = new Thread(m, "System 2");
		t1.start();
		t2.start();
	}
}
class SaleTickets2 implements Runnable {
	private String ticketNo = "800800"; // 車票編號
	private int ticket = 1; // 共享私有成員,編號爲800800的車票數量爲1
	public void run() {
		System.out.println(Thread.currentThread().getName()+ " is saling Ticket " + ticketNo); 
		sale();
	}
	public synchronized void sale() {// 同步方法中的代碼爲關鍵代碼
		if (ticket > 0) {
			try {// 休眠0-1000毫秒,用來模擬網絡延遲
				Thread.sleep((int) (Math.random() * 1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			ticket = ticket - 1; // 修改車票數據庫的信息
			System.out.println("ticket is saled by "
					+ Thread.currentThread().getName() + ", amount is: "
					+ ticket);
		} else {// 顯示當前該車票的預訂情況
			System.out.println("Sorry " + Thread.currentThread().getName()
					+ ",Ticket " + ticketNo + " is saled");
		}
	}
}


public class SynchronizeDemo2 {
	public static void main(String[] args) {
		SaleTickets3 m = new SaleTickets3();
		Thread t1 = new Thread(m, "System 1");
		Thread t2 = new Thread(m, "System 2");
		t1.start();
		t2.start();
	}

}
class SaleTickets3 implements Runnable {
	private String ticketNo = "800800"; // 車票編號
	private int ticket = 1; // 共享私有成員,編號爲800800的車票數量爲1
	public void run() {
	   System.out.println(Thread.currentThread().getName()+ " is saling Ticket " + ticketNo);
		synchronized (this) {
			if (ticket > 0) {
				try { // 休眠0-1000毫秒,用來模擬網絡延遲
					Thread.sleep((int) (Math.random() * 1000));
				} catch (InterruptedException e) {
				}
				ticket = ticket - 1; // 修改車票數據庫的信息
				System.out.println("ticket is saled by "
						+ Thread.currentThread().getName() + ", amount is: "+ ticket);
			} else {// 顯示當前該車票的預訂情況
				System.out.println("Sorry " + Thread.currentThread().getName()
						+ ",Ticket " + ticketNo + " is saled");
			}
		}
	}
}

線程死鎖


當兩個或多個線程等待一個不可能滿足的條件時會發生死鎖。
死鎖是發生在線程間相互阻塞的現象,允許多個線程併發訪問共享資源時,必須提供同步機制,然而如果對這種機制使用不當,可能會出現線程永遠被阻塞的現象。
例如兩個線程分別等待對方各自佔有的一個資源,就會產生死鎖。
Java本身既不能預防死鎖,也不能發現死鎖,只能靠程序設計上的謹慎來避免

import static java.lang.System.out;
class Share{
	private Share share;
	public void setShare(Share share) {
		this.share = share;
	}
	public synchronized void getBook() throws InterruptedException{
		out.println("第一個線程得到書!");
		Thread.sleep(500);
		share.getPictureByFirstThread();
	}
	public synchronized void getBookBySecondThread() throws InterruptedException{
		out.println("第二個線程得到書!");
	}
	public synchronized void getPicture() throws InterruptedException{
		out.println("第二個線程得到畫!");
		Thread.sleep(500);
		share.getBookBySecondThread();
	}
	public synchronized void getPictureByFirstThread() throws InterruptedException{
		out.println("第一個線程得到畫!");
	}
	public void notSynMethod(){
		out.println("非同步方法");
	}
}


public class MyThread extends Thread{
	private Share share;
	private boolean firstThread;
	public MyThread(Share share,boolean firstThread){
		this.share=share;
		this.firstThread=firstThread;
	}
	public void run() {
		try {
			if(firstThread){
				share.getBook();
			}else{
				share.getPicture();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		Share book=new Share();
		Share picture=new Share();
		book.setShare(picture);
		picture.setShare(book);
		Thread t1=new MyThread(book,true);
		Thread t2=new MyThread(picture,false);
		t1.start();t2.start();//啓動兩個線程
	}
}




線程間通信-wait-notify 機制

Java提供了3個非常重要的方法來巧妙地解決線程間的通信問題。這3個方法分別是:wait()、notify() 和notifyAll()。

wait():等待方法。可讓當前線程放棄監視器並進入睡眠狀態,直到其他線程進入同一監視器並調用notify()方法爲止

notify():喚醒方法。可喚醒同一對象監視器中調用了wait()方法的隨機線程,並把它移入鎖申請隊列。

notifyAll():喚醒所有線程的方法。喚醒同一對象監視器中調用了wait()方法的所有線程,具有最高優先級的線程首先被喚醒。


水泵與水池問題就是經典的用來說明線程間通信的問題
     一個水池,一個進水泵,一個出水泵;進水泵往水池中注入水,出水泵則將水池中的水抽出去,其中只有當水池中存在水時出水泵纔可能工作,同樣,也只有當水池在未滿狀態下,進水泵才能工作,當進出水泵以非穩態流方式運行時則水池中的水隨時可能發生溢出和被抽乾的情況,這就需要DCS系統調控機制來動態偵測水位情況並實時控制進出水泵的啓停狀態

這個問題有許多實現方法。下面示例中顯示了利用wait-notify 機制解決水泵與水池問題的代碼。

class Pool{//水池類
	private int maxLevel;//水池最高水位(米)
	private int waterLevel;//動態水位高度(米)
	public Pool(int maxLevel,int waterLevel){
		this.maxLevel=maxLevel;
		this.waterLevel=waterLevel;
	}
	public int getWaterLevel() {
		return waterLevel;
	}
	public synchronized void inWater() throws InterruptedException{
		if(waterLevel==maxLevel)this.wait();
		waterLevel++;
		this.notify();
		System.out.println("進水後水位:"+waterLevel);
	}
	public synchronized void outWater() throws InterruptedException{
		if(waterLevel==0)this.wait();
		waterLevel--;
		this.notify();
		System.out.println("出水後水位:"+waterLevel);
	}
}


class WaterPump implements Runnable{//水泵類
	private Pool pool;//兩個水泵共享的水池對象
	private boolean isInWaterPump;//是否爲進水泵
	public WaterPump(Pool pool,boolean isInWaterPump){
		this.pool=pool;
		this.isInWaterPump=isInWaterPump;
	}
	public void run() {
		try {
			if(isInWaterPump){//進水泵進水兩百次
				for(int i=0;i<200;i++)pool.inWater();
			}else{//出水泵出水兩百次
				for(int i=0;i<200;i++)pool.outWater();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
public class TestSynThread{//測試類
	public static void main(String[] args) {
		Pool pool=new Pool(50,20);//水池對象,水位初始高度20米,最高不超過50米
		WaterPump inWaterPump=new WaterPump(pool,true);//進水泵對象
		WaterPump outWaterPump=new WaterPump(pool,false);//出水泵對象
		Thread in=new Thread(inWaterPump,"進水泵線程");
		Thread out=new Thread(outWaterPump,"出水泵線程");
		in.start();//啓動進水泵
		out.start();//啓動出水泵
	}
}



 守護線程:

守護線程是這樣的一種線程:它不需要進程等待它的結束,即:如果一個JVM進程中全部是守護線程時(沒有任何用戶線程的時候)該JVM進程將直接結束,只要有一個用戶線程存在則JVM進程就不會結束
我們熟知的JVM中的垃圾回收機制就是一個守護線程,它在不斷的跟蹤判斷一個對象是否還存在到根級路徑(棧或方法區)的引用,如果不存在根級引用則該線程將回收該對象佔用的內存空間
一個Java程序在啓動之後(即一個JVM進程啓動後)至少存在兩個線程,一個是主線程main,它是一個用戶線程,另外一個是垃圾回收子線程,它是一個守護線程

import static java.lang.System.out;
class DaemonThread extends Thread{
	private boolean isFirst;
	public DaemonThread(boolean isFirst){
		this.isFirst=isFirst;
	}
	public void run() {
		if(isFirst){
			for(int i=0;i<100;i++){
				out.println("第一個線程:"+i);
			}
		}else{
			for(int i=0;i<100;i++){
				out.println("第二個線程:"+i);
			}
		}
	}
}
public class TestDaemonThread{
	public static void main(String[] args) {
		Thread t1=new DaemonThread(true);
		Thread t2=new DaemonThread(false);
		t1.setDaemon(true);
		t2.setDaemon(true);
		t1.start();
		t2.start();
	}
}



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