Java學習筆記(1)——進程與線程,多進程的創建方式

面試中經常被問到:

        什麼是進程?什麼是線程?兩者的區別?

感覺這個問題網上有很多版本,So我把它們收集起來,黑體句子是我現在這個水平贊同的說法。

       1.進程是資源分配的最小單位(操作系統教材裏的說法);是正在運行的程序實例;是構成運行程序的資源的集合(C#圖解教程裏的說法);每個正在系統上運行的程序都是一個進程;

       2.線程是CPU調度的最小單位(同操作系統教材裏的說法);是一組指令的集合,或者是程序的特殊段,它可以在程序裏獨立執行;也可以把它理解爲代碼運行的上下文。

       3.兩者關係:默認情況下,一個進程中只包含一個線程(主線程)且至少有一個線程,但一個進程可以包含多個線程。線程基本上是輕量級的進程,進程是所有線程的集合,每一個線程是進程中的一條執行路徑。

 

       爲什麼用到多線程(多線程的好處)? 提高程序效率和響應性,充分利用計算資源(多核cpu)。舉例: 迅雷多線程下載、分批發送短信等。

      多線程不好的地方:首先會帶來線程安全問題,線程切換會帶來一定的系統消耗。

        Java中(多)線程創建方式:

         ①繼承Thread類(不推薦,因爲Java不支持多繼承,繼承會讓類間的耦合性變高):

public class Main {
	public static void main(String[] args) {
		// 創建線程
		Thread myThread = new MyThread();
		
		// 啓動線程(不要直接調用run方法,不然程序就是線性執行了)
		myThread.start();
		
		// 輸出"當前線程"的線程名稱
		System.out.printf("1.Welcome! I'm %s\n", Thread.currentThread().getName());
	}
}

// 定義繼承Thread類的子類
class MyThread extends Thread {
	
	// 複寫run方法,方法中實現了線程的任務處理邏輯
	@Override
	public void run() {
		System.out.printf("2.Welcome! I'm %s\n", Thread.currentThread().getName());
	}
}

         ②實現Runnable接口(這種方式比較推薦,面向接口編程):

public class Main {
	public static void main(String[] args) {
		// 創建線程
		Thread myThread = new Thread(new MyThread());
		
		// 啓動線程(不要直接調用run方法,不然程序就是線性執行了)
		myThread.start();
		
		// 輸出"當前線程"的線程名稱
		System.out.printf("1.Welcome! I'm %s\n", Thread.currentThread().getName());
	}
}

// 定義繼承Thread類的子類
class MyThread implements Runnable {
	
	// 複寫run方法,方法中實現了線程的任務處理邏輯
	@Override
	public void run() {
		System.out.printf("2.Welcome! I'm %s\n", Thread.currentThread().getName());
	}
}

         值得一提的是,主線程和子線程的執行順序並不是固定了,和寫的順序無關。比如上面的例子,main和Thread-0的打印順序並不是固定的,這個和操作系統的調度有關。

         ③匿名內部類(這種和第二種類似,優點是少聲明類):

public class Main {
	public static void main(String[] args) {
		Thread myThread = new Thread(new Runnable() {

			@Override
			public void run() {

				for (int i = 10; i > 0; i--) {
					System.out.println("sub:" + i);
					int n = 10 / i;
				}
			}
			
		});
		myThread.start();
		for (int i = 10; i > 0; i--) {
			System.out.println("main:" + i);
			int n = 10 / i;
		}
	}
}

         也可以用Java8的lambda表達式創建:

		Thread myThread = new Thread(()-> {
			for (int i = 10; i > 0; i--) {
				System.out.println("sub:" + i);
				int n = 10 / i;
			}
		  
		});

       如果多運行幾次,會發現每次結果都不一樣,而且大多數情況main和sub交替執行,這就和之前的順序執行區別開來。

      下面說明線程間不受影響性。將main線程和子線程中循環變量i的的終止條件都改爲i > -10,讓子線程運行到半路拋異常。運行程序後發現子線程運行到i = 0就退出了,而main線程不受影響:

public class Main {
	public static void main(String[] args) {
		Thread myThread = new Thread(()-> {
			for (int i = 10; i > -10; i--) {
				System.out.println("sub:" + i);
			    int n = 10 / i;
			}
		  
		});
		myThread.start();
		for (int i = 10; i > -10; i--) {
			System.out.println("main:" + i);
			// int n = 10 / i;
		}
	}
}

       分析運行結果可以知道,當sub中的i減小到0後,main線程佔用cpu,一直減少到3,然後sub線程又拿到cpu,10 / 0拋出異常,後續的代碼停止執行。而主線程繼續執行到循環體結束。如果在main線程中去掉註釋,在sub線程中註釋,結果就反過來,main線程執行到一半卡殼,而sub線程繼續執行完。這一定程度上體現了線程間互不干擾性。

        ④線程池技術(線程池是管理併發執行任務個數的理想方法;當你有多個任務需要創建多個線程,考慮使用線程池,更加高效)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
	public static void main(String[] args) {
		ExecutorService executor = Executors.newFixedThreadPool(3);
		executor.execute(new MyThread());
		executor.execute(new MyThread());
		executor.execute(new MyThread());
		executor.shutdown();
		for (int i = 5; i > 0; i--) {
			System.out.println("main:" + i);
		}
	}
}

// 定義繼承Thread類的子類
class MyThread implements Runnable {
	
	@Override
	public void run() {
		for (int i = 5; i > 0; i--) {
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}
	}
}

         

       這只是簡單使用了一下線程池技術,更多用法參考Oracle的jdk文檔。

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