1. 程式人生 > >一個程式完全入門Java多執行緒

一個程式完全入門Java多執行緒

程式碼只供學習使用,實際開發中建議遵守Java開發規範,合理分包

程式碼所涉及知識點:

什麼是執行緒、Thread方法和Runnable介面的介紹及建立執行緒、執行緒的狀態和生命週期、sleep方法和join方法的使用、執行緒的優先順序、執行緒同步、執行緒間通訊(見另一篇文章,點選訪問)

package practice;

/**
 * 
 * @author slvayf
 * 
 */

// 通過繼承Thread類的方式,可以完成多執行緒的建立,多執行緒由系統排程交叉執行
class MyThread extends Thread {
	// 父類引用,傳參修改執行緒名稱
	public MyThread(String name) {
		super(name);
	}

	public void run() {
		for (int i = 1; i < 6; i++) {
			// sleep方法:
			// 通過sleep方法使執行緒休眠(暫停執行緒的執行1毫秒)
			// 但是暫停1毫秒後,執行緒並不會馬上進入執行狀態,而是進入可執行狀態(Runnable)
			// 執行thread類的sleep方法,多次執行程式我們可以發現:
			// ___“通過Runnable介面建立的執行緒”因為不休眠而“通過thread方法建立的執行緒:執行緒0、1、2”1毫秒休眠一次
			// 所以“通過Runnable介面建立的執行緒”獲得CPU使用權開始執行的概率會更大
			// 不過,因為“通過thread方法建立的執行緒:執行緒0”執行時雖然1毫秒休眠一次,但因為使用join方法,它是優先執行的
			// “通過Runnable介面建立的執行緒”也可以使用thread類的sleep方法實現類似效果,join相同
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				// 休眠中斷異常
				e.printStackTrace();
			}
			System.out.println(getName() + "正在執行第" + i + "次");
		}
	}
}

// 但是繼承Thread類這種方式有一個侷限性,如果一個類已經有了自己的父類,就不可以繼承Thread類,因為java規定類是單繼承的
// 如果該類中的還有部分程式碼需要被多個執行緒同時執行,只有對該類進行額外的功能擴充套件,java就提供了一個介面Runnable
// Runnable介面中定義了run方法,其實run方法的定義就是為了儲存多執行緒要執行的程式碼
// 所以,通常建立執行緒都用第二種方式,因為,實現Runnable介面可以避免單繼承的侷限性
// 而且,繼承Thread,是可以對Thread類中的方法進行子類複寫的
// 但是不需要做這個複寫動作,只為定義執行緒程式碼存放位置的話,實現Runnable介面更方便一些
// 所以,Runnable介面將執行緒要執行的任務封裝成了物件
// 其實,將多執行緒要執行的程式碼的位置單獨定義到介面中,為其他類進行功能擴充套件提供了前提
// 所以,Thread類在描述執行緒時,內部定義的run方法,也來自於Runnable介面
class PrintRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 1; i < 6; i++) {
			System.out.println(Thread.currentThread().getName() + "正在執行第" + i + "次"+ " 優先順序:" + Thread.currentThread().getPriority());
			// 主執行緒和新建立執行緒預設優先順序為5
			// 設定t2的優先順序為10,多次執行下來我們可以發現,在大多數情況下,t2是要比t1優先執行的,優先順序先10後5
			// 但有時候t1("通過Runnable介面建立的執行緒:執行緒1")還是在t2之前執行
			// 其原因是,作業系統確定程式執行優先順序,並不是只根據使用者設定的優先順序,還要綜合計算機記憶體及其它資源利用狀況,做出綜合判斷
			// 比如先啟動的低優先順序的程式t1,由於所需資源和執行時間少,可能在很短時間內全部執行完成
		}
	}
}

// 兩個執行緒同步(執行緒互斥)的測試類,連續兩次對100減10,每減一次輸出一次,區別如下:
// ThreadSynchronization1物件未鎖定,兩個執行緒隨機執行,執行次數無法計數故由執行緒名輸入,結果為 90 90,結果異常
// ThreadSynchronization2物件鎖定(使用synchronized關鍵字),兩個執行緒順序執行,因為是順序執行,所以執行次數可以累加(k),結果為 90 80,結果正常
// synchronized關鍵字可以用在成員方法、靜態方法和程式碼塊
class ThreadSynchronization1 implements Runnable {
	private int i = 100, j = 10, ans;

	@Override
	public void run() {
		ans = this.i - j;
		// 執行緒休眠1秒,模擬執行緒異常
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.i = ans;
		System.out.println("未鎖定ThreadSynchronization的物件時第" + Thread.currentThread().getName() + "次減10:" + this.i);
	}
}

class ThreadSynchronization2 implements Runnable {
	private int i = 100, j = 10, ans, k = 1;

	@Override
	public synchronized void run() {
		ans = this.i - j;
		// 執行緒休眠1秒,模擬執行緒異常
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.i = ans;
		System.out.println("鎖定ThreadSynchronization的物件後第" + k + "次減10:" + this.i);
		k++;
	}
}

public class MultiThreading {
	public static void main(String[] args) {
		// 檢視主執行緒優先順序
		System.out.println("--主執行緒的優先順序為:" + Thread.currentThread().getPriority());
		// MyThread、PrintRunnable類的物件例項化
		MyThread mt0 = new MyThread("通過thread方法建立的執行緒,使用join搶先執行:執行緒0");
		MyThread mt1 = new MyThread("通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1");
		MyThread mt2 = new MyThread("通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2");
		PrintRunnable pr1 = new PrintRunnable();
		PrintRunnable pr2 = new PrintRunnable();
		ThreadSynchronization1 ts1 = new ThreadSynchronization1();
		ThreadSynchronization2 ts2 = new ThreadSynchronization2();
		// 建立Thread類物件,引數1為:基於Runnable介面實現類的物件,引數2為:執行緒名
		Thread t1 = new Thread(pr1, "通過Runnable介面建立的執行緒:執行緒1");
		Thread t2 = new Thread(pr2, "通過Runnable介面建立的執行緒:執行緒2");
		Thread tsTest1 = new Thread(ts1, "1");
		Thread tsTest2 = new Thread(ts1, "2");
		Thread tsTest3 = new Thread(ts2);
		Thread tsTest4 = new Thread(ts2);
		// 啟動執行緒(Thread類的start方法),使執行緒進入可執行(Runnable)狀態
		mt0.start();
		// 呼叫join方法,mt0搶佔資源優先執行
		// mt0.join(10); 指:mt0執行緒在優先執行的情況下執行10毫秒,不論是否執行完畢,都將讓出自己佔用的資源,退出優先狀態
		try {
			mt0.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		mt1.start();
		mt2.start();
		t1.start();
		t2.start();
		// 設定t2的優先順序最高,為10
		t2.setPriority(10);
		// 啟動執行緒同步測試
		tsTest1.start();
		tsTest2.start();
		tsTest3.start();
		tsTest4.start();
	}
}

一個可參考的執行結果:(為方便觀察,輸出結果做了空行分隔處理)

--主執行緒的優先順序為:5

通過thread方法建立的執行緒,使用join搶先執行:執行緒0正在執行第1次
通過thread方法建立的執行緒,使用join搶先執行:執行緒0正在執行第2次
通過thread方法建立的執行緒,使用join搶先執行:執行緒0正在執行第3次
通過thread方法建立的執行緒,使用join搶先執行:執行緒0正在執行第4次
通過thread方法建立的執行緒,使用join搶先執行:執行緒0正在執行第5次

通過Runnable介面建立的執行緒:執行緒2正在執行第1次 優先順序:10
通過Runnable介面建立的執行緒:執行緒2正在執行第2次 優先順序:10
通過Runnable介面建立的執行緒:執行緒2正在執行第3次 優先順序:10
通過Runnable介面建立的執行緒:執行緒2正在執行第4次 優先順序:10
通過Runnable介面建立的執行緒:執行緒2正在執行第5次 優先順序:10

通過Runnable介面建立的執行緒:執行緒1正在執行第1次 優先順序:5

通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1正在執行第1次

通過Runnable介面建立的執行緒:執行緒1正在執行第2次 優先順序:5
通過Runnable介面建立的執行緒:執行緒1正在執行第3次 優先順序:5
通過Runnable介面建立的執行緒:執行緒1正在執行第4次 優先順序:5
通過Runnable介面建立的執行緒:執行緒1正在執行第5次 優先順序:5

通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1正在執行第2次
通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2正在執行第1次
通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2正在執行第2次
通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1正在執行第3次
通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2正在執行第3次
通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1正在執行第4次
通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1正在執行第5次
通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2正在執行第4次
通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2正在執行第5次

未鎖定ThreadSynchronization的物件時第1次減10:90
未鎖定ThreadSynchronization的物件時第2次減10:90
鎖定ThreadSynchronization的物件後第1次減10:90
鎖定ThreadSynchronization的物件後第2次減10:80