併發與並行 併發:指兩個或多個事件在同一個時間段內發生。 並行:指兩個或多個事件在同一時刻發生(同時發生) 在操作系統中,安裝了多個程式,併發指的是在一段時間內巨集觀上有多個程式同時運行,這在單CPU系統中,每一時刻只能有一道程式執行,即微觀上這些程式是分時的交替運行,只不過是給人的感覺是同時運行,那 ...
併發與並行
併發:指兩個或多個事件在同一個時間段內發生。
並行:指兩個或多個事件在同一時刻發生(同時發生)
在操作系統中,安裝了多個程式,併發指的是在一段時間內巨集觀上有多個程式同時運行,這在單CPU系統中,每一時刻只能有一道程式執行,即微觀上這些程式是分時的交替運行,只不過是給人的感覺是同時運行,那是因為分時交替運行的時間是非常短的、而在多個CPU系統中,則這些可以併發執行的程式便可以分配到多個處理器上(CPU),實現多任務並行執行, 即利用每個處理器來處理一個可以併發執行的程式,這樣多個程式便可以同時執行。目前電腦市場上說的多核 CPU,便是多核處理器,核越多,並行處理的程式越多,能大大的提高電腦運行的效率。
線程調度
單核處理器的電腦肯定是不能並行的處理多個任務的,只能是多個任務在單個CPU上併發運行。同理,線程也是一樣的,從巨集觀角度上理解線程是並行運行的,但是從微觀角度上分析卻是串列運行的,即一個線程一個線程的去運行,當系統只有一個CPU時,線程會以某種順序執行多個線程,我們把這種情況稱之為線程調度。
分類:
分時調度:所有線程輪流使用 CPU 的使用權,平均分配每個線程占用CPU的時間。
搶占式調度:優先讓優先順序高的線程使用 CPU,如果線程的優先順序相同,那麼會隨機選擇一個(線程隨機性),Java使用的為搶占式調度
搶占式調度詳解
大部分操作系統都支持多進程併發運行,現在的操作系統幾乎都支持同時運行多個程式。比如:現在我 們上課一邊使用編輯器,一邊使用錄屏軟體,同時還開著畫圖板,dos視窗等軟體。此時,這些程式是 在同時運行,”感覺這些軟體好像在同一時刻運行著“。 實際上,CPU(中央處理器)使用搶占式調度模式在多個線程間進行著高速的切換。對於CPU的一個核而 言,某個時刻,只能執行一個線程,而 CPU的在多個線程間切換速度相對我們的感覺要快,看上去就是 在同一時刻運行。 其實,多線程程式並不能提高程式的運行速度,但能夠提高程式運行效率,讓CPU的 使用率更高。
線程與進程
進程:是指一個記憶體中運行的應用程式,每個進程都有一個獨立的記憶體空間,一個應用程式可以同時運行多個進程;進程也是程式的一次執行過程,是系統運行程式的基本單位;系統運行一個程式即是一個進程從創建、運行到消亡的過程。
線程:線程是進程中的一個執行單元,負責當前進程中程式的執行,一個進程中至少有一個線程。一個進程中是可以有多個線程的,這個應用程式也可以稱之為多線程程式。
總之:一個程式運行後至少有一個進程,一個進程中可以包含多個線程
創建線程類
Java使用 java.lang.Thread 類代表線程,所有的線程對象都必須是Thread類或其子類的實例。每個線程的作用是 完成一定的任務,實際上就是執行一段程式流即一段順序執行的代碼。Java使用線程執行體來代表這段程式流。 Java中通過繼承Thread類來創建並啟動多線程的步驟如下:
1. 定義Thread類的子類,並重寫該類的run()方法,該run()方法的方法體就代表了線程需要完成的任務,因此把 run()方法稱為線程執行體。
2. 創建Thread子類的實例,即創建了線程對象
3. 調用線程對象的start()方法來啟動該線程
1 package demosummary.thread;
2
3 public class MyThread extends Thread {
4 //定義線程名稱的構造方法
5 public MyThread(String name) {
6 super(name);
7 }
8 //重寫run方法
9 @Override
10 public void run() {
11 for (int i = 0; i < 5; i++) {
12 System.out.println(getName() + ":正在執行第" + i + "線程");
13 }
14 }
15
16 public static void main(String[] args) {
17 //創建線程對象
18 MyThread myThread = new MyThread("新的線程");
19 //開啟線程
20 myThread.start();
21 //執行for迴圈
22 for (int i = 0; i < 5; i++) {
23 System.out.println("正在執行主線程:"+i);
24 }
25 }
26 }
多線程原理
1 package demosummary.thread;
2
3 public class MyThread extends Thread {
4 //定義線程名稱的構造方法
5 public MyThread(String name) {
6 super(name);
7 }
8 //重寫run方法
9 @Override
10 public void run() {
11 for (int i = 0; i < 5; i++) {
12 System.out.println(getName() + ":正在執行第" + i + "線程");
13 }
14 }
15
16 public static void main(String[] args) {
17 System.out.println("這裡是main線程");
18 //創建線程對象
19 MyThread myThread = new MyThread("小強");
20 //開啟線程
21 myThread.start();
22 //執行for迴圈
23 for (int i = 0; i < 5; i++) {
24 System.out.println("旺財:"+i);
25 }
26 }
27 }
程式啟動運行main時候,java虛擬機啟動一個進程,主線程main在main()調用時候被創建。隨著調用mt的對象的 start方法,另外一個新的線程也啟動了,這樣,整個應用就在多線程下運行,多線程執行時,在棧記憶體中,其實每一個執行線程都有一片自己所屬的棧記憶體空間。進行方法的壓棧和彈棧
Thread類
構造方法
public Thread() :分配一個新的線程對象。
public Thread(String name) :分配一個指定名字的新的線程對象。
public Thread(Runnable target) :分配一個帶有指定目標新的線程對象。
public Thread(Runnable target,String name) :分配一個帶有指定目標新的線程對象並指定名字。
常用方法
public String getName() :獲取當前線程名稱。
public void start() :導致此線程開始執行; Java虛擬機調用此線程的run方法。
public void run() :此線程要執行的任務在此處定義代碼。
public static void sleep(long millis) :使當前正在執行的線程以指定的毫秒數暫停(暫時停止執行)。
public static Thread currentThread() :返回對當前正在執行的線程對象的引用。
創建線程方式有兩種,一種是繼承Thread類方式,一種是實現Runnable介面方式
創建線程(實現Runnable介面)
採用 java.lang.Runnable 也是非常常見的一種,我們只需要重寫run方法即可
方法:
1. 定義Runnable介面的實現類,並重寫該介面的run()方法,該run()方法的方法體同樣是該線程的線程執行體。
2. 創建Runnable實現類的實例,並以此實例作為Thread的target來創建Thread對象,該Thread對象才是真正 的線程對象。
3. 調用線程對象的start()方法來啟動線程。
1 package demosummary.thread;
2
3 public class MyRunnable implements Runnable {
4 //重寫run方法
5 @Override
6 public void run() {
7 for (int i = 0; i < 5; i++) {
8 System.out.println(Thread.currentThread().getName() + "---" + i);
9 }
10 }
11
12 public static void main(String[] args) {
13 //創建類任務對象
14 MyRunnable myRunnable = new MyRunnable();
15 //創建線程
16 Thread thread = new Thread(myRunnable, "德瑪");
17 //開啟線程
18 thread.start();
19 for (int i = 0; i < 5; i++) {
20 System.out.println("皇子" + "---" + i);
21 }
22 }
23 }
通過實現Runnable介面,使得該類有了多線程類的特征。run()方法是多線程程式的一個執行目標。所有的多線程 代碼都在run方法裡面。Thread類實際上也是實現了Runnable介面的類。 在啟動的多線程的時候,需要先通過Thread類的構造方法Thread(Runnable target) 構造出對象,然後調用Thread 對象的start()方法來運行多線程代碼。
實際上所有的多線程代碼都是通過運行Thread的start()方法來運行的。因此,不管是繼承Thread類還是實現 Runnable介面來實現多線程,最終還是通過Thread的對象的API來控制線程的,熟悉Thread類的API是進行多線程 編程的基礎
註意:Runnable對象僅僅作為Thread對象的target,Runnable實現類里包含的run()方法僅作為線程執行體。 而實際的線程對象依然是Thread實例,只是該Thread線程負責執行其target的run()方法
Thread和Runnable的區別
實現Runnable介面比繼承Thread類所具有的優勢:
1. 適合多個相同的程式代碼的線程去共用同一個資源。
2. 可以避免java中的單繼承的局限性。
3. 增加程式的健壯性,實現解耦操作,代碼可以被多個線程共用,代碼和線程獨立。
4. 線程池只能放入實現Runable或Callable類線程,不能直接放入繼承Thread的類。
註意:在java中,每次程式運行至少啟動2個線程。一個是main線程,一個是垃圾收集線程。因為每當使用 java命令執行一個類的時候,實際上都會啟動一個JVM,每一個JVM其實在就是在操作系統中啟動了一個進程
匿名內部類方式實現線程的創建
使用線程的內匿名內部類方式,可以方便的實現每個線程執行不同的線程任務操作
1 package demosummary.thread;
2
3 public class InnerClassThread {
4 public static void main(String[] args) {
5 MyRunnable myRunnable = new MyRunnable(){
6 public void run(){
7 for (int i = 1; i <= 5; i++) {
8 System.out.println("德瑪---"+i);
9 }
10 }
11 };
12 new Thread(myRunnable).start();
13 }
14 }