Java基础----【异常、线程】
主要內容
- 異常、線程
第一章 異常
1.1 異常概念
異常,就是不正常的意思。在生活中:醫生說,你的身體某個部位有異常,該部位和正常相比有點不同,該部位的功能將受影響.在程序中的意思就是:
- 異常 :指的是程序在執行過程中,出現的非正常的情況,最終會導致JVM的非正常停止。
在Java等面向對象的編程語言中,異常本身是一個類,產生異常就是創建異常對象并拋出了一個異常對象。Java處理異常的方式是中斷處理。
異常指的并不是語法錯誤,語法錯了,編譯不通過,不會產生字節碼文件,根本不能運行.
1.2 異常體系
異常機制其實是幫助我們找到程序中的問題,異常的根類是java.lang.Throwable,其下有兩個子類:java.lang.Error與java.lang.Exception,平常所說的異常指java.lang.Exception。
Throwable體系:
- Error:嚴重錯誤Error,無法通過處理的錯誤,只能事先避免,好比絕癥。
- Exception:表示異常,異常產生后程序員可以通過代碼的方式糾正,使程序繼續運行,是必須要處理的。好比感冒、闌尾炎。
Throwable中的常用方法:
-
public void printStackTrace():打印異常的詳細信息。
包含了異常的類型,異常的原因,還包括異常出現的位置,在開發和調試階段,都得使用printStackTrace。
-
public String getMessage():獲取發生異常的原因。
提示給用戶的時候,就提示錯誤原因。
-
public String toString():獲取異常的類型和異常描述信息(不用)。
出現異常,不要緊張,把異常的簡單類名,拷貝到API中去查。
1.3 異常分類
我們平常說的異常就是指Exception,因為這類異常一旦出現,我們就要對代碼進行更正,修復程序。
異常(Exception)的分類:根據在編譯時期還是運行時期去檢查異常?
- 編譯時期異常: checked異常。在編譯時期,就會檢查,如果沒有處理異常,則編譯失敗。(如日期格式化異常)
- 運行時期異常: runtime異常。在運行時期,檢查異常.在編譯時期,運行異常不會被編譯器檢測(不報錯)。(如數學異常)
1.4 異常的產生過程解析
先運行下面的程序,程序會產生一個數組索引越界異常ArrayIndexOfBoundsException。我們通過圖解來解析下異常產生的過程。
工具類
// 數組工具類 : public class ArrayTools {// 行為 : 根據傳入的數組與下標, 返回指定的元素public static int getElement(int[] arr, int index) {// 2. 正常邏輯代碼 ...int element = arr[index];return element;} }測試類
public class ArrayToolsTest1 {public static void main(String[] args) {// 定義一個數組int[] arr = {10, 20, 30};// 調用工具類的方法int element = ArrayTools.getElement(null, 2);// 查看結果System.out.println("element = " + element);System.out.println("程序執行完畢 ...");} }上述程序執行過程圖解:
第二章 異常的處理
Java異常處理的五個關鍵字:try、catch、finally、throw、throws
2.1 拋出異常throw
在編寫程序時,我們必須要考慮程序出現問題的情況。比如,在定義方法時,方法需要接收參數。那么,當調用方法使用接收到的參數時,首先需要先對參數數據進行合法的判斷,數據若不合法,就應該告訴調用者,傳遞合法的數據進來。這時需要使用拋出異常的方式來告訴調用者。
在java中,提供了一個throw關鍵字,它用來拋出一個指定的異常對象。那么,拋出一個異常具體如何操作呢?
創建一個異常對象。封裝一些提示信息(信息可以自己編寫)。
需要將這個異常對象告知給調用者。怎么告知呢?怎么將這個異常對象傳遞到調用者處呢?通過關鍵字throw就可以完成。throw 異常對象。
throw用在方法內,用來拋出一個異常對象,將這個異常對象傳遞到調用者處,并結束當前方法的執行。
使用格式:
throw new 異常類名(參數);例如:
throw new NullPointerException("要訪問的arr數組不存在");throw new ArrayIndexOutOfBoundsException("該索引在數組中不存在,已超出范圍");學習完拋出異常的格式后,我們通過下面程序演示下throw的使用。
// 數組工具類 : public class ArrayTools {// 行為 : 根據傳入的數組與下標, 返回指定的元素public static int getElement(int[] arr, int index) {// 0.1 判斷, arr 是否為 nullif (arr == null) {// 0.2 拋出一個 `異常`// 程序一旦執行到 throw 代碼, 后續代碼就不執行了, 此時, 程序就會直接返回到調用處.throw new NullPointerException("數組引用不能為空, 請修正代碼.");}// 1.1 判斷, index 是否越界if (index < 0 || index >= arr.length) {// 1.2 拋出一個 `異常`, 提前終止程序的執行// 格式 : throw new 異常類型(自定義信息);throw new ArrayIndexOutOfBoundsException("數組下標越界異常, 請修正代碼.");}// 2. 正常邏輯代碼 ...int element = arr[index];return element;} }public class ArrayToolsTest1 {public static void main(String[] args) {/*String str = "abcdefg";// StringIndexOutOfBoundsExceptionchar c = str.charAt(str.length());System.out.println("c = " + c);*/// 定義一個數組int[] arr = {10, 20, 30};// 調用工具類的方法int element = ArrayTools.getElement(null, 2);// 查看結果System.out.println("element = " + element);System.out.println("程序執行完畢 ...");} }輸出結果 : Exception in thread "main" java.lang.NullPointerException: 數組引用不能為空, 請修正代碼.注意:如果產生了問題,我們就會throw將問題描述類即異常進行拋出,也就是將問題返回給該方法的調用者。
那么對于調用者來說,該怎么處理呢?一種是進行捕獲處理,另一種就是繼續將問題聲明出去,使用throws聲明處理。
2.2 Objects非空判斷
還記得我們學習過一個類Objects嗎,曾經提到過它由一些靜態的實用方法組成,這些方法是null-save(空指針安全的)或null-tolerant(容忍空指針的),那么在它的源碼中,對對象為null的值進行了拋出異常操作。
- public static <T> T requireNonNull(T obj):查看指定引用對象不是null。
查看源碼發現這里對為null的進行了拋出異常操作:
public static <T> T requireNonNull(T obj) {if (obj == null)throw new NullPointerException();return obj; } import java.util.Objects;// 數組工具類 : public class ArrayTools {// 行為 : 根據傳入的數組與下標, 返回指定的元素public static int getElement(int[] arr, int index) {// 0.1 判斷, arr 是否為 null/*if (arr == null) {// 0.2 拋出一個 `異常`// 程序一旦執行到 throw 代碼, 后續代碼就不執行了, 此時, 程序就會直接返回到調用處.throw new NullPointerException("數組引用不能為空, 請修正代碼.");}*/// Objects 工具類提供了一個方法, 可以針對 `對象` 進行空引用判斷.// 含義 : 判斷 arr 對象是否為空, 如果為空, 底層就會拋出一個 `空引用` 類型.// Objects.requireNonNull(arr);Objects.requireNonNull(arr, "數組引用不能為空, 請修正代碼.");// 1.1 判斷, index 是否越界if (index < 0 || index >= arr.length) {// 1.2 拋出一個 `異常`, 提前終止程序的執行// 格式 : throw new 異常類型(自定義信息);throw new ArrayIndexOutOfBoundsException("數組下標越界異常, 請修正代碼.");}// 2. 正常邏輯代碼 ...int element = arr[index];return element;} }2.3 聲明異常throws
聲明異常:將問題標識出來,報告給調用者。如果方法內通過throw拋出了編譯時異常,而沒有捕獲處理(稍后講解該方式),那么必須通過throws進行聲明,讓調用者去處理。
關鍵字throws運用于方法聲明之上,用于表示當前方法不處理異常,而是提醒該方法的調用者來處理異常(拋出異常).
聲明異常格式:
修飾符 返回值類型 方法名(參數) throws 異常類名1,異常類名2…{ }聲明異常的代碼演示:
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;public class ThrowsExceptionTest2 {public static void main(String[] args) throws ParseException {String str = "2018-08-08";parseDate(str);}public static void parseDate(String str) throws ParseException {// 1. 創建一個日期格式化對象SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");// 2. 解析Date date = df.parse(str);// 3. 查看System.out.println("date = " + date);} }2.4 捕獲異常try…catch
如果異常出現的話,會立刻終止程序,所以我們得處理異常:
try-catch的方式就是捕獲異常。
- 捕獲異常:Java中對異常有針對性的語句進行捕獲,可以對出現的異常進行指定方式的處理。
捕獲異常語法如下:
try{編寫可能會出現異常的代碼 }catch(異常類型 e){處理異常的代碼//記錄日志/打印異常信息/繼續拋出異常 }**try:**該代碼塊中編寫可能產生異常的代碼。
**catch:**用來進行某種異常的捕獲,實現對捕獲到的異常進行處理。
注意:try和catch都不能單獨使用,必須連用。
演示如下:
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;public class ExceptionTest1 {public static void main(String[] args) {// 需求 : 將一個字符串解析為一個 `Date` 對象String str1 = "2018年08月08日";String str2 = "2018-08-08";// 調用方法時, 只要調用了 `聲明編譯時期異常` 的方法, 此時, 程序就會報錯.// 含義 : 編譯器問調用者, 你調用一個帶編譯時期的異常方法, 如果發生了該異常, 請問, 您如何處理呢 ???// 回答1 : 編譯器兄弟呀, 如果發生了異常, 請將該問題交給我的調用者去處理. (繼續聲明 throws)// 回答2 : 編譯器兄弟呀, 如果發生了該異常, 請將該異常交給我來處理. 不管我怎么處理, 都與你無關. (異常捕獲 try-catch)/*try {// 編寫可能會發生異常的代碼 ...} catch (異常類型 exception) {// 如果發生了該異常, 這個大括號中就是對該異常進行的處理方案代碼 ... (不管寫什么. 都是處理方案代碼, 哪怕什么都不寫)} finally {// 無論是否發生異常, 都會被執行到的代碼 ...}*/try {parseStringToDate(str1);} catch (ParseException e) {// 說明 : catch 語句必須發生異常后, 才可以進入.// 打印日志 ... 處理邏輯 ...// System.out.println("發生了解析異常. 傳入字符串日期格式錯誤. 格式: yyyy-MM-dd.");// 含義 : 輸出異常發生的堆棧信息e.printStackTrace(); // Throwable 類定義的方法.// getMassage & toString()String message = e.getMessage();String str = e.toString();System.out.println("message = " + message);System.out.println("str = " + str);}// 后續代碼 ...int num1 = 10;int num2 = 20;int sum = num1 + num2;System.out.println("sum = " + sum);System.out.println("程序正常執行完畢 ...");}// 異常聲明 : 位置 (方法參數列表之后) 格式 : throws 異常類型public static void parseStringToDate(String str) throws ParseException {// 1. 創建一個 `簡單日期格式化` 對象SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");// 2. 將傳入的參數實現解析// unhandled exception : java.text.parseException 未處理的異常, 解析異常.// 說明1 : parseException 是編譯時期的異常.// 說明2 : NullPointerException / IndexOutOfBoundsException 屬于運行時異常.// 說明3 : 如果異常類型為運行時異常, 編譯器不檢查該類異常, 因此, 程序可以選擇不處理.// 說明4 : 如果異常類型為編譯時異常, 編譯器會檢查該類異常, 因此, 程序必須做出處理. 不處理的話, 編譯報錯, 程序無法運行.// 處理有兩種方式 : 1. 繼續拋 (繼續聲明 throws) 2. 異常捕獲. (try-catch)// 1. 繼續拋 (繼續聲明 throws) 含義 : 這個異常問題, 我不處理, 交給我的調用者進行處理.// 2. 異常捕獲. (try-catch) 含義 : 這個問題交給我來處理, 不用調用者關心.Date date = df.parse(str);// 查看結果System.out.println("date = " + date);} }輸出結果 : java.text.ParseException: Unparseable date: "2018年08月08日"at java.base/java.text.DateFormat.parse(DateFormat.java:388)at cn.itcast.exception2.ExceptionTest1.parseStringToDate(ExceptionTest1.java:68)at cn.itcast.exception2.ExceptionTest1.main(ExceptionTest1.java:28) message = Unparseable date: "2018年08月08日" str = java.text.ParseException: Unparseable date: "2018年08月08日" sum = 30 程序正常執行完畢 ...如何獲取異常信息:
Throwable類中定義了一些查看方法:
-
public String getMessage():獲取異常的描述信息,原因(提示給用戶的時候,就提示錯誤原因。
-
public String toString():獲取異常的類型和異常描述信息(不用)。
-
public void printStackTrace():打印異常的跟蹤棧信息并輸出到控制臺。
2.4 finally 代碼塊
finally:有一些特定的代碼無論異常是否發生,都需要執行。另外,因為異常會引發程序跳轉,導致有些語句執行不到。而finally就是解決這個問題的,在finally代碼塊中存放的代碼都是一定會被執行的。
什么時候的代碼必須最終執行?
當我們在try語句塊中打開了一些物理資源(磁盤文件/網絡連接/數據庫連接等),我們都得在使用完之后,最終關閉打開的資源。
finally的語法:
try…catch…finally:自身需要處理異常,最終還得關閉資源。
注意:finally不能單獨使用。
比如在我們之后學習的IO流中,當打開了一個關聯文件的資源,最后程序不管結果如何,都需要把這個資源關閉掉。
finally代碼參考如下:
import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException;public class ExceptionTest1 {public static void main(String[] args) {String result = readFile("a.txt");System.out.println("result = " + result);}// 需求 : IO (BufferedReader & BufferedWriter)// 說明 : 定義一個方法, 讀取指定文件中的數據, 并返回是否讀取成功字符串.public static String readFile(String fileName) {// 1. 讀取文件// 說明 : 創建 FileReader 時, 會發生一個 `FileNotFoundException` 文件找不到異常.// 選擇捕獲異常 : try - catchFileReader reader = null;try {reader = new FileReader(fileName);} catch (FileNotFoundException e) {System.out.println("發生了文件找不到異常, 請檢查文件路徑或文件名稱.");// 如果程序進入 catch 語句, 說明程序發生了異常. 文件讀取失敗了.return "文件讀取失敗"; // return 之前會先執行 finally 代碼塊.} finally {// finally 代碼塊就是為 `關閉資源` 而設計. (硬件文件資源, 數據庫資源, 網絡資源 ...)// 說明 : finally 塊的設計就是為了解決 catch 塊中 return 語句的問題.System.out.println("finally 代碼塊被執行 ...");// 說明 : 如果一個對象的默認值為null, 調用時, 必須要做 null 判斷.if (reader != null) {try {reader.close();} catch (IOException exception) {// 忽略 ...}}// finally 塊中, 在開發時, 絕對不會出現 return 語句, 因為 finally 塊就是用來關閉資源的, 而不是返回結果的.// 如果要返回結果, 請在 catch 語句中返回.// return "哈哈哈哈";}return "文件讀取成功";} }輸出結果 : 發生了文件找不到異常, 請檢查文件路徑或文件名稱. finally 代碼塊被執行 ... result = 文件讀取失敗當只有在try或者catch中調用退出JVM的相關方法,此時finally才不會執行,否則finally永遠會執行。
給大家一張好記的圖 , 哈哈
2.5 異常注意事項
- 運行時異常被拋出可以不處理。即不捕獲也不聲明拋出。
- 如果父類拋出了多個異常,子類覆蓋父類方法時,只能拋出相同的異常或者是他的子集。
- 父類方法沒有拋出異常,子類覆蓋父類該方法時也不可拋出異常。此時子類產生該異常,只能捕獲處理,不能聲明拋出
- 在try/catch后可以追加finally代碼塊,其中的代碼一定會被執行,通常用于資源回收。
子父類
public class Person {public void introduce() {System.out.println("大家好, 我是 Person 類.");} }public class Student extends Person {@Overridepublic void introduce() {System.out.println("大家好, 我是 Student 類.");// 子類重寫父類的方法, 如果父類被重寫的方法沒有聲明編譯時期異常, 子類重寫時, 不可以拋出編譯時期異常.// 如果有異常, 子類重寫方法中, 只能選擇 try-catch 語句進行捕獲.try {readFile();} catch (FileNotFoundException e) {System.out.println("文件找不到異常.");}}public void readFile() throws FileNotFoundException {FileReader reader = new FileReader("a.txt");} }接口與接口實現類
public interface MyRunnable {void run(); }public class MyRunnableImpl implements MyRunnable {@Overridepublic void run() {System.out.println("MyRunnableImpl run ...");// 實現類重寫接口中抽象方法時, 如果該抽象方法沒有聲明編譯時期異常, 實現類重寫也不能聲明該異常.// 如果內部有編譯時期異常, 實現類需要在內部使用 try-catch 自行處理.try {readFile();} catch (FileNotFoundException e) {System.out.println("文件不存在, 讀取失敗!");}}public void readFile() throws FileNotFoundException {FileReader reader = new FileReader("a.txt");} }第三章 自定義異常
3.1 概述
為什么需要自定義異常類:
我們說了Java中不同的異常類,分別表示著某一種具體的異常情況,那么在開發中總是有些異常情況是SUN沒有定義好的,此時我們根據自己業務的異常情況來定義異常類。,例如年齡負數問題,考試成績負數問題。
在上述代碼中,發現這些異常都是JDK內部定義好的,但是實際開發中也會出現很多異常,這些異常很可能在JDK中沒有定義過,例如年齡負數問題,考試成績負數問題.那么能不能自己定義異常呢?
什么是自定義異常類:
在開發中根據自己業務的異常情況來定義異常類.
自定義一個業務邏輯異常: RegisterException。一個登陸異常類。
異常類如何定義:
3.2 自定義異常的練習
要求:我們模擬登陸操作,如果用戶名已存在,則拋出異常并提示:親,該用戶名已經被注冊。
首先定義一個登陸異常類RegisterException:
// 自定義了一個 `注冊異常` 類 // 說明 : 能拋出的對象都必須繼承自 Throwable 類. // 說明 : 如果是自定異常, 不直接繼承 Throwble 類, 可以選擇繼承 `Exception / RuntimeException` 這兩個類. public class RegisterException extends Exception {// 提供兩個構造方法即可.public RegisterException() {super();}public RegisterException(String message) {super(message);} }模擬登陸操作,使用數組模擬數據庫中存儲的數據,并提供當前注冊賬號是否存在方法用于判斷。
public class RegisterTest {// 屬性 : 數據庫private static String[] names = {"Jack", "Peter", "Tom"};public static void main(String[] args) {try {boolean result = checkUserExists("Jackie");System.out.println("result = " + result);} catch (RegisterException e) {e.printStackTrace();}System.out.println("程序正常執行完畢 ...");}// 行為 : 查看該用戶是否已經存在, 如果存在, 不能再注冊public static boolean checkUserExists(String username) throws RegisterException {// 判斷, 遍歷 namesfor (String name : names) {if (name.equals(username)) {// 用戶名已經存在, 不能再注冊, 需要拋出一個 `RegisterException` 異常.// 說明 : 自己 throw 出來的異常, 只需要聲明一下即可.throw new RegisterException("用戶是否已經存在, 請重新選擇.");}}return true;} }3.3 異常流程的思考 :
思考程序運行時輸出的序號語句 :
public class ThinkingException {public static void main(String[] args) {System.out.println(1);try {System.out.println(2);int[] arr = {10, 20, 30};System.out.println(arr[3]); // ArrayIndexOutOfBoundsException: 3System.out.println(3);String str1 = "";System.out.println(str1.toString());System.out.println(4);String str2 = null;System.out.println(str2.toString()); // NullPointerExceptionSystem.out.println(5);int num = 10 / 0;System.out.println("num = " + num); // ArithmeticException: / by zeroSystem.out.println(6);Object obj = new Object();// 這不是多態 : 子類引用 -> 父類對象 (類型轉換錯誤)String str = (String) obj; // ClassCastException:System.out.println("str = " + str);System.out.println(7);} catch (ArrayIndexOutOfBoundsException e) {System.out.println(8);} catch (NullPointerException e) {System.out.println(9);} catch (ArithmeticException e) {System.out.println(10);} catch (ClassCastException e) {System.out.println(11);} finally {System.out.println(12);}System.out.println(13);} }輸出結果 : 1 2 8 12 13說明 : 當多異常處理時,捕獲處理,前邊的類不能是后邊類的父類
注意:這種異常處理方式,要求多個catch中的異常不能相同,并且若catch中的多個異常之間有子父類異常的關系,那么子類異常要求在上面的catch處理,父類異常在下面的catch處理。
public class ThinkintExceptionTest2 {public static void main(String[] args) {// 父類 : Exception -> 子類 : IOException -> 子子類 : FileNotFoundExceptiontry {FileReader reader = new FileReader("a.txt");reader.read();} catch (FileNotFoundException e) { // 子子類System.out.println("FileNotFoundException 被捕獲 ...");} catch (IOException e) { // 子類System.out.println("IOException 被捕獲 ...");} catch (Exception e) { // 父類System.out.println("Exception 被捕獲 ...");}} }第四章 多線程
多線程 : 理解為程序中的多條執行路徑.
迅雷 : 下載電影, 同時下載多部電影, 顯示的結果好像是多部電影同時下載.
說明 : 迅雷也是一個程序, 那么一個程序中只存在一個 main 方法. 就一個入口, 如何實現多條執行路徑呢 ???
思考 : 同一個Java程序, 可不可以同時執行多個死循環 ???
public class ThreadTest1 {public static void main(String[] args) {// 思考 : 同一個 Java 程序中, 是否可以同時執行多個死循環 ???// 死循環 : (耗時操作)// `常量` 編譯器知道數值. `變量` 編譯器是不知道數值.// 為了解決這種現實生活中的問題, Java 出現 `多線程` 的語法.// 網絡聊天 (發送 / 接收) 上傳下載 (程序的多條執行路徑)boolean flag = true;while (flag) {// 電影 1 (1G -> 5分鐘)System.out.println("電影 1 下載中 ...");}while (true) {// 電影 2 (1G -> 5分鐘)System.out.println("電影 2 下載中 ...");}} }4.1 并發與并行
- 并行:指兩個或多個事件在同一時刻發生(同時發生)。
- 并發:指兩個或多個事件在同一個時間段內發生。
在操作系統中,安裝了多個程序,并發指的是在一段時間內宏觀上有多個程序同時運行,這在單 CPU 系統中,每一時刻只能有一道程序執行,即微觀上這些程序是分時的交替運行,只不過是給人的感覺是同時運行,那是因為分時交替運行的時間是非常短的。
而在多個 CPU 系統中,則這些可以并發執行的程序便可以分配到多個處理器上(CPU),實現多任務并行執行,即利用每個處理器來處理一個可以并發執行的程序,這樣多個程序便可以同時執行。目前電腦市場上說的多核 CPU,便是多核處理器,核越多,并行處理的程序越多,能大大的提高電腦運行的效率。
注意:單核處理器的計算機肯定是不能并行的處理多個任務的,只能是多個任務在單個CPU上并發運行。同理,線程也是一樣的,從宏觀角度上理解線程是并行運行的,但是從微觀角度上分析卻是串行運行的,即一個線程一個線程的去運行,當系統只有一個CPU時,線程會以某種順序執行多個線程,我們把這種情況稱之為線程調度。
4.2 線程與進程
- 進程:是指一個內存中運行的應用程序,每個進程都有一個獨立的內存空間,一個應用程序可以同時運行多個進程;進程也是程序的一次執行過程,是系統運行程序的基本單位;系統運行一個程序即是一個進程從創建、運行到消亡的過程。
- 線程:進程內部的一個獨立執行單元;一個進程可以同時并發的運行多個線程,可以理解為一個進程便相當于一個單 CPU 操作系統,而線程便是這個系統中運行的多個任務。
- 進程就理解為 正在執行的程序. 線程就類似于進程中的小進程.
- 進程一般有 操作系統 實現分配, 但是 線程 是由程序編寫中實現分配的.
我們可以再電腦底部任務欄,右鍵----->打開任務管理器,可以查看當前任務的進程和線程 :
進程
線程
進程與線程的區別
- 進程:有獨立的內存空間,進程中的數據存放空間(堆空間和棧空間)是獨立的,至少有一個線程。
- 線程:堆空間是共享的,棧空間是獨立的,線程消耗的資源比進程小的多。
- 堆空間是對象, 棧空間是方法. 對象數據是可以共享的, 而方法中局部變量的數據是獨立的. 不能共享.
注意:
線程調度:
計算機通常只有一個CPU時,在任意時刻只能執行一條計算機指令,每一個進程只有獲得CPU的使用權才能執行指令。所謂多進程并發運行,從宏觀上看,其實是各個進程輪流獲得CPU的使用權,分別執行各自的任務。那么,在可運行池中,會有多個線程處于就緒狀態等到CPU,JVM就負責了線程的調度。JVM采用的是搶占式調度,沒有采用分時調度,因此可以能造成多線程執行結果的的隨機性。
4.3 創建線程類
Java使用java.lang.Thread類代表線程,所有的線程對象都必須是Thread類或其子類的實例。每個線程的作用是完成一定的任務,實際上就是執行一段程序流即一段順序執行的代碼。Java使用線程執行體來代表這段程序流。Java中通過繼承Thread類來創建并啟動多線程的步驟如下:
代碼如下:
測試類:
/* 1. 自定義一個類, 繼承 Thread 類. (線程類) 2. 自定義類重寫 Thread 類的 run() 方法. 將耗時代碼編寫到 run() 方法實現體中. 3. 創建一個自定義類的對象. 4. 調用自定義對象的 start() 方法, 啟動線程.*/public class ThreadTest2 {// Java虛擬機在程序運行時, 自動啟動了一條線程 (主線程)public static void main(String[] args) {// 第一條子線程 ...// 3. 創建一個自定義類的對象.DownloadThread1 downloadThread1 = new DownloadThread1();// 4. 啟動downloadThread1.start();// 第二條子線程 ...DownloadThread2 downloadThread2 = new DownloadThread2();downloadThread2.start();// 主線程中運行的代碼 ...while (true) {System.out.println("主線程中運行的代碼 ...");}} }自定義線程類:
public class DownloadThread1 extends Thread {@Overridepublic void run() {while (true) {// 電影 1 (1G -> 5分鐘)System.out.println("電影 1 下載中 ...");}} }public class DownloadThread2 extends Thread {@Overridepublic void run() {while (true) {// 電影 2 (1G -> 5分鐘)System.out.println("電影 2 下載中 ...");}} }OK 相信我們都看到多線程的現象了,那么接下來幾天我們就進入多線程的世界!
那么現在有個小問題,請問植物大戰僵尸游戲中,是用了多進程設計程序呢,還是多線程?
總結
以上是生活随笔為你收集整理的Java基础----【异常、线程】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转】葡萄酒的干型、半干型、半甜型、甜型
- 下一篇: CentOS8迁移TencentOS 3