今天我們先來聊聊有關線程的話題...... 一. 線程概述 1. 簡單區分程式、進程和線程 程式是指一段靜態的代碼 進程是指正在執行的程式,將靜態的代碼運行起來 線程是指正在執行程式的小單元 舉個慄子,班級準備大掃除,在大掃除之前,老師在紙上列了一個清單,每個同學都有不同的工作任務,分配好任務之後, ...
一. 線程概述
1. 簡單區分程式、進程和線程
程式是指一段靜態的代碼
進程是指正在執行的程式,將靜態的代碼運行起來
線程是指正在執行程式的小單元
舉個慄子,班級準備大掃除,在大掃除之前,老師在紙上列了一個清單,每個同學都有不同的工作任務,分配好任務之後,每個同學都是有條不紊地完成自己的任務,掃地的同學去掃地,擦黑板的同學去擦黑板,清理桌子的同學清理桌子......在這個例子里,這個清單就是程式,而這個班級的全體同學是一個整體,也就是一個進程,最後,這個班級裡面一個個同學就是一個個線程。
2. 理解進程
理解線程之前,先簡單理解一下進程。進程的三大特征:獨立性、動態性、併發性。
獨立性:指進程是系統中獨立存在的實體,擁有獨立的資源(eg:私有的地址空間)。
動態性:這是相對於程式而言的,程式是一段靜態的代碼,而進程是活動的,擁有自己的生命周期。
併發性:多個進程可以在單個處理器上併發執行,互不影響。
還是上面那慄子,這個班級就是一個進程,他是一個整體,他擁有自己的教室,有自己的班級名字,這裡可以體現出獨立性。這個班級的全體人員按照的任務清單干活,直至把教室打掃乾凈(即完成任務),這裡可以體現出動態性。併發性呢,首先這個班級不只有一個,還有好多其他的班級,他們也可以打掃他們自己的教室,互不影響。
3. 理解線程
線程是進程的執行單元,在程式中,線程是獨立的、併發的執行流。
線程的特點:
-
每個線程有自己的堆棧,自己程式計數器,自己的局部變數,這裡體現了線程的獨立性。
-
相同父進程下的所有線程共用進程獨立的記憶體單元(eg:代碼段、進程的共有數據),為此可以實現線程間的相互通信。
-
多個線程之間也可以併發執行,互不影響。
4. 多線程 VS 多進程
-
線程之間可以共用記憶體,而進程之間不可以。
-
系統創建線程代價比較小,而且多線程是實現多任務併發比多進程的效率更高。
-
Java語言內置了多線程功能,簡化了Java多線程編程。
二. 線程的創建和啟動
1. 繼承Thread類創建線程類
步驟:
-
定義一個線程類,需繼承Thread類。
-
重寫父類的run( )方法,此方法是線程執行體,供cpu自動調用(cpu會用調度策略去處理就緒狀態的線程)。
-
創建線程類的實例對象,調用start( )方法,這個方法告訴cpu這個線程對象進入就緒狀態。
1 package com.hx.thread; 2 3 // 1.定義一個線程類,需繼承Thread類。 4 public class MyThread1 extends Thread { 5 // 2.重寫run方法 6 public void run() { 7 for (int i = 0; i < 100; i++) { 8 System.out.println(Thread.currentThread().getName() + " " + i); 9 try { 10 Thread.sleep(10); 11 } catch (Exception e) { 12 e.printStackTrace(); 13 } 14 } 15 } 16 17 public static void main(String[] args) throws Exception { 18 // 3.創建線程實例,調用start方法,進入就緒狀態,交給cpu 19 MyThread1 myThread1 = new MyThread1(); 20 myThread1.start(); 21 for (int i = 0; i < 100; i++) { 22 System.out.println(Thread.currentThread().getName() + " " + i); 23 Thread.sleep(10); 24 } 25 } 26 }
2. 實現Runnable介面創建線程類
步驟:
-
定義一個線程類,需實現Runnable介面。
-
實現介面的run( )方法,此方法是線程執行體,供cpu自動調用(cpu會用調度策略去處理就緒狀態的線程)。
-
創建線程類的實例對象。可是Runnable沒有start( )方法,因此需要第4步。
-
創建一個Thread對象(真正的線程對象),用來包裝上面的那個實例對象,然後調用start( )方法。
1 package com.hx.thread; 2 3 // 1.定義一個線程類,需實現Runnable介面。 4 public class MyThread2 implements Runnable { 5 // 2.實現介面的run( )方法 6 @Override 7 public void run() { 8 for (int i = 0; i < 100; i++) { 9 System.out.println(Thread.currentThread().getName() + " " + i); 10 try { 11 Thread.sleep(10); 12 } catch (Exception e) { 13 e.printStackTrace(); 14 } 15 } 16 } 17 18 public static void main(String[] args) throws Exception { 19 // 3.創建線程類的實例對象 20 MyThread2 myThread2 = new MyThread2(); 21 // 4.創建一個Thread對象(真正的線程對象),用來包裝上面的那個實例對象,然後調用start( )方法。 22 Thread t = new Thread(myThread2); 23 t.start(); 24 for (int i = 0; i < 100; i++) { 25 System.out.println(Thread.currentThread().getName() + " " + i); 26 Thread.sleep(10); 27 } 28 } 29 }
3. 實現Callable介面創建線程類
步驟:
-
定義一個線程類,需實現Callable介面。
-
實現Callable介面的call( )方法,此方法是線程執行體。
-
創建線程類的實例對象。
-
創建FutureTask的對象來包裝線程類實例對象。
-
創建Thread的對象來包裝Future類的實例對象。
1 package com.hx.thread; 2 import java.util.concurrent.Callable; 3 import java.util.concurrent.FutureTask; 4 5 // 1.定義一個線程類,需實現Callable介面 6 public class MyThread3 implements Callable { 7 // 2.實現Callable介面的call()方法 8 @Override 9 public String call() throws Exception { 10 for (int i = 0; i < 100; i++) { 11 System.out.println(Thread.currentThread().getName() + " " + i); 12 Thread.sleep(10); 13 } 14 return Thread.currentThread().getName(); 15 } 16 17 public static void main(String[] args) throws Exception { 18 // 3.創建線程類的實例對象 19 MyThread3 myThread3 = new MyThread3(); 20 // 4.創建FutureTask的實例對象來包裝線程類實例對象 21 FutureTask futureTask = new FutureTask(myThread3); 22 // 5.創建Thread的實例對象來包裝Future類的實例對象 23 Thread t = new Thread(futureTask); 24 t.start(); 25 for (int i = 0; i < 100; i++) { 26 System.out.println(Thread.currentThread().getName() + " " + i); 27 Thread.sleep(10); 28 } 29 // 列印出call()方法的返回值 30 System.out.println(futureTask.get()); 31 } 32 }
4. 三種方式的對比
-
採用繼承Thread類這種方式來創建線程,編寫簡單,可是由於Java不支持多繼承,所以不能再繼承其他父類。
-
採用實現Runnable介面或Callable介面,可以繼承其他類,多個線程可以共用同一個target對象,非常適合多個線程來處理同一資源的情況,可以更好地體現面向對象的特點,不過編寫比較複雜。
-
採用實現Callable介面,call( )方法是線程執行體,有返回值,可以拋出異常,其功能比run( )方法更強大。
三. 線程的生命周期
四. 控制線程
join( ):讓一個線程等待另外一個線程完成的方法,當在某個程式執行流中調用其他線程的join方法,調用線程將被阻塞,直至join線程完成。
setDaemon(true):指定線程設置為後臺線程。在start( )之前調用。後臺線程,又稱守護線程、精靈線程,其作用是為其他線程提供服務(eg:JVM的垃圾回收線程),如果所有的前臺線程都死亡,後臺線程也會自動死亡。
sleep( long time):設置線程睡眠時間,參數單位為毫秒,調用此方法線程進入阻塞狀態。
setPriority(int newPriority):設置優先順序,參數範圍:1-10,一般使用三個靜態常量(MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORITY)。
嘻嘻,今天的內容就先到這吧,歡迎大家前來留言。
由於現在個人水平有限,文章若存不當之處,還請各位大佬們加以斧正。