1.異常
根類Throwable體系:java
- Error:嚴重錯誤,程序自身已經不能處理的問題,出現的嚴重錯誤程序終止運行
- Exception:編譯期異常,這種異常是強制咱們使用catch捕獲處理或throws拋出給調用者。你遇到這種異常必須進行catch或throws,若是不處理,編譯器會報錯。
- RuntimeExeption:Exception的子類,運行時異常,這種異常咱們不須要處理,徹底由虛擬機接管。
- NullPointerException:RuntimeExeption的子類,空指針異常
- RuntimeExeption:Exception的子類,運行時異常,這種異常咱們不須要處理,徹底由虛擬機接管。
異常處理的五個關鍵字:try,catch,finally,throw,throws數據庫
(1)拋出異常 throw:
語法:throw new 異常類名(參數)數組
例子:throw new NullPointerException(「要訪問的數組不存在」)安全
注意:多線程
- throw關鍵字必須放在方法的內部
- throw關鍵字後邊new的對象必須是Exception或Exception子類對象
- throw關鍵字拋出指定的異常對象,咱們就必須處理這個異常對象
- throw關鍵字後邊建立的是RuntimeException或RuntimeException的子類對象,咱們能夠不處理默認交給JVM處理(打印異常對象,中斷程序)
- throw關鍵字後邊建立的是編譯異常,咱們處理這個異常,要麼throws,要麼try...catch
Objects的非空判斷:併發
Objects.requireNonNull(obj,message),用來判斷obj是否爲空,爲空則拋出異常,信息爲messageide
(2)throws關鍵字:異常處理的第一種方式,交給別人處理
做用:將異常拋出給方法的調用者處理,最終交給JVM處理-->中斷處理工具
使用格式:在方法聲明時使用測試
修飾符 返回值類型 方法名(參數列表) throws 異常1,異常2...{ throw new 異常1("xxx"); throw new 異常2("xxx"); ... }
注意:ui
- 必須寫在方法聲明處
- 聲明的異常必須是Exception或Exception子類對象
- 方法內部拋出多個異常,聲明處必須也要聲明多個異常,若是拋出父異常也拋出子異常則只要聲明父異常便可
- 咱們必須處理聲明的異常
- 要麼交給方法調用者處理,最終交給JVM
- 要麼使用try...catch本身處理異常
(3)如何獲取異常信息
Throwable中定義了一些查看異常的方法:
- public String getMessage():獲取異常描述信息
- public string toString():獲取異常的類型和異常描述信息
- public void printStackTrace():打印異常的跟蹤棧信息並輸出到控制檯
(4)try,catch,finally
語法:
try{ 可能出現異常的代碼 }catch(異常類1 變量名){ 異常處理的邏輯 } ... catch(異常類n 變量名){ }finally{ 不管是否出現異常都會執行 }
注意:
- finally不能單獨使用,必須和try一塊兒使用
- finally通常用於資源釋放
- 若是finally中有return則永遠返回finally中的結果,咱們須要避免出現return。
- 當一個catch捕獲來處理異常後就不會調用其餘catch處理異常了
- 多個catch捕獲異常,咱們先進行子異常捕獲處理,若是沒有子異常咱們就進行父異常捕獲處理
(5)異常注意事項
- 多個異常分別處理:多個try..catch 處理
- 多個異常一次捕獲,屢次處理:一個try多個catch處理,子異常在前,父異常在後
- 多個異常一次捕獲,一次處理:一個try...catch,異常爲全部異常的父類或自己
- 子父類異常:
- 若是父類拋出多個異常,子類重寫父類方法時,拋出和父類相同的異常或者是父類異常的子類或者不拋出異常
- 父類方法沒有拋出異常,子類重寫父類方法時也可不拋出異常,此時子類產生異常,只能捕獲異常,不能聲明拋出
1 class Father{ 2 public void show01() throws NullPointerException,ClassCastException{} 3 public void show02() throws IndexOutOfBoundsException{} 4 public void show03() throws IndexOutOfBoundsException{} 5 public void show04() {} 6 } 7 8 class Son extends Father{ 9 //子類重寫父類方法時,拋出和父類相同的異常 10 public void show01() throws NullPointerException,ClassCastException{} 11 //子類重寫父類方法時,拋出父類異常的子類 12 public void show02() throws IndexOutOfBoundsException{} 13 //子類重寫父類方法時,不拋出異常 14 public void show03() throws IndexOutOfBoundsException{} 15 //子類重寫父類方法時,父類沒有拋出異常,子類本身處理異常 16 public void show04() { 17 try { 18 throw new Exception("出現異常"); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } 22 } 23 }
2.自定義異常類
- 繼承Exception處理方式:
- 第一種:拋出異常給調用者,須要聲明
- 第二種:本身處理
- 繼承RuntimeException處理方式:
- 直接拋出,不用聲明直接交給JVM處理
1 public class demo02 { 2 3 4 //拋出異常給調用者處理,須要聲明 5 public static void testException1() throws TestException { 6 System.out.println("這是testException1"); 7 throw new TestException("testException1"); 8 } 9 10 11 //拋出異常本身處理 12 public static void testException2() { 13 System.out.println("這是testException2"); 14 try { 15 throw new TestException("testException1"); 16 } catch (TestException e) { 17 e.printStackTrace(); 18 return ; //用於結束方法 19 } 20 21 } 22 23 //運行時異常不用處理和聲明,交給JVM處理,最終中斷處理 24 public static void testRuntimeException(){ 25 System.out.println("這是testRuntimeException"); 26 throw new TestRuntimeException("testRuntimeException"); 27 } 28 29 public static void main(String[] args) throws TestException { 30 31 demo02.testException1(); 32 demo02.testException2(); 33 demo02.testRuntimeException(); 34 35 } 36 }
TestException
1 public class TestException extends Exception { 2 3 public TestException() { 4 super(); 5 } 6 7 public TestException(String message) { 8 super(message); 9 } 10 }
TestRuntimeException
1 public class TestRuntimeException extends RuntimeException{ 2 3 public TestRuntimeException() { 4 super(); 5 } 6 7 public TestRuntimeException(String message) { 8 super(message); 9 } 10 }
補充:Java常見異常
1. RuntimeException子類:
序號 | 異常名稱 | 異常描述 |
---|---|---|
1 | java.lang.ArrayIndexOutOfBoundsException | 數組索引越界異常。當對數組的索引值爲負數或大於等於數組大小時拋出。 |
2 | java.lang.ArithmeticException | 算術條件異常。譬如:整數除零等。 |
3 | java.lang.SecurityException | 安全性異常 |
4 | java.lang.IllegalArgumentException | 非法參數異常 |
5 | java.lang.ArrayStoreException | 數組中包含不兼容的值拋出的異常 |
6 | java.lang.NegativeArraySizeException | 數組長度爲負異常 |
7 | java.lang.NullPointerException | 空指針異常。當應用試圖在要求使用對象的地方使用了null時,拋出該異常。譬如:調用null對象的實例方法、訪問null對象的屬性、計算null對象的長度、使用throw語句拋出null等等。 |
2.IOException
序號 | 異常名稱 | 異常描述 |
---|---|---|
1 | IOException | 操做輸入流和輸出流時可能出現的異常 |
2 | EOFException | 文件已結束異常 |
3 | FileNotFoundException | 文件未找到異常 |
3. 其餘
序號 | 異常名稱 | 異常描述 |
---|---|---|
1 | ClassCastException | 類型轉換異常類 |
2 | ArrayStoreException | 數組中包含不兼容的值拋出的異常 |
3 | SQLException | 操做數據庫異常類 |
4 | NoSuchFieldException | 字段未找到異常 |
5 | NoSuchMethodException | 方法未找到拋出的異常 |
6 | NumberFormatException | 字符串轉換爲數字拋出的異常 |
7 | StringIndexOutOfBoundsException | 字符串索引超出範圍拋出的異常 |
8 | IllegalAccessException | 不容許訪問某類異常 |
9 | InstantiationException | 當應用程序試圖使用Class類中的newInstance()方法建立 一個類的實例,而指定的類對象沒法被實例化時,拋出該異常 |
10 | java.lang.ClassNotFoundException | 找不到類異常。當應用試圖根據字符串形式的類名構造類,而在遍歷CLASSPAH以後找不到對應名稱的class文件時,拋出該異常。 |
3.多線程
(1)Thread類:
構造方法:
- public Thread():分配一個新的線程對象
- public Thread(String name):分配一個指定名字的新的線程對象
- public Thread(Runnable target):分配一個帶有指定目標新的線程對象
- public Thread(Runnable target,String name):分配一個帶有指定目標新的線程對象並指定名字
經常使用方法:
- public String getName():獲取當前線程名稱
- public void setName():設置當前線程名稱
- public void start():讓線程開始執行,Java虛擬機調用該線程的run方法
- public void run():該線程要執行的任務在此處定義代碼
- public static void sleep(long millis):是當前正在執行的線程以指定的毫秒數暫停
- public static Thread currentThread():返回對當前正在執行的線程對象的引用
MyThread類:
1 public class MyThread extends Thread { 2 3 public MyThread() { 4 } 5 6 public MyThread(String name) { 7 super(name); 8 } 9 10 @Override 11 public void run() { 12 13 System.out.println(Thread.currentThread().getName()); 14 } 15 }
demo01類:
1 public class demo01 { 2 3 4 public static void main(String[] args) { 5 6 //1.修改線程名稱方法一 7 MyThread thread1 = new MyThread(); 8 thread1.setName("thread1"); 9 thread1.start(); //thread1 10 11 //修改線程名稱方法二 12 MyThread thread2 = new MyThread("thread2"); 13 thread2.start(); //thread2 14 15 16 for (int i = 0; i < 60; i++) { 17 System.out.println(i); 18 19 //2.使當前線程睡眠1秒執行一次 20 try { 21 Thread.sleep(1000); 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 } 26 27 28 } 29 }
(2)Runnable接口:
實現步驟:
- 建立一個Runnable接口的實現類
- 在實現類中重寫Runnable接口的run方法,設置線程任務
- 建立一個Runnable接口的實現類對象
- 建立Thread類對象,構造方法中傳遞Runnable接口實現類對象
- 調用start方法,開啓新線程執行run方法
RunnableImpl類:
1 public class RunnableImpl implements Runnable { 2 @Override 3 public void run() { 4 5 for (int i = 0; i < 60; i++) { 6 System.out.println(Thread.currentThread().getName() + ":" + i); 7 } 8 } 9 }
demo02類:
1 public class demo02 { 2 3 public static void main(String[] args) { 4 5 RunnableImpl runnable = new RunnableImpl(); 6 new Thread(runnable).start(); 7 8 for (int i = 0; i < 60; i++) { 9 10 System.out.println(Thread.currentThread().getName() + ":" + i); 11 12 } 13 } 14 }
(3)實現Runnable接口和繼承Thread類比較
實現Runnable接口優勢:
- 避免了單繼承的侷限性
- 加強了程序的擴展性,下降了程序的耦合性:把設置線程任務和開啓線程分離
(4)匿名內部類實現線程的建立
優勢:簡化代碼
1 public class demo03 { 2 3 public static void main(String[] args) { 4 5 //匿名Thread 6 new Thread() { 7 @Override 8 public void run() { 9 10 for (int i = 0; i < 20; i++) { 11 System.out.println("Thread:" + i); 12 } 13 } 14 }.start(); 15 16 //匿名Runnable 17 new Thread(new Runnable() { 18 @Override 19 public void run() { 20 for (int i = 0; i < 20; i++) { 21 System.out.println("Runnable:" + i); 22 } 23 } 24 }).start(); 25 } 26 }
4.併發和並行
併發:多個任務在同一時間段執行,任務交替執行
並行:多個任務在同一時刻執行,任務同時執行
線程:進程中的一個執行單元
進程:內存中運行的一個應用程序
(1)線程安全
1.同步代碼塊:
synchronized (同步鎖){ 須要同步操做的代碼(訪問了共享數據的代碼) }
保證了線程安全,可是頻繁的判斷鎖,釋放鎖和獲取鎖致使程序的效率下降
RunableImpl類:this指RunnableImpl建立的對象
1 public class RunnableImpl implements Runnable { 2 3 private int num = 100; 4 5 @Override 6 public void run() { 7 8 while (true){ 9 10 synchronized (this){ 11 if (num > 0){ 12 try { 13 Thread.sleep(10); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 System.out.println(Thread.currentThread().getName() + "得到當前數字爲:" + num); 18 num--; 19 } 20 } 21 } 22 23 } 24 }
demo04類:
1 public class demo04 { 2 3 public static void main(String[] args) { 4 5 RunnableImpl runnable = new RunnableImpl(); 6 new Thread(runnable).start(); 7 new Thread(runnable).start(); 8 new Thread(runnable).start(); 9 10 11 } 12 }
2.同步方法:
public synchronized void method(){ 可能會產生線程安全的代碼 }
保證了線程安全,可是頻繁的判斷鎖,釋放鎖和獲取鎖致使程序的效率下降
RunnableImpl類:this指RunnableImpl建立的對象
1 public class RunnableImpl implements Runnable { 2 3 private int num = 100; 4 5 @Override 6 public void run() { 7 8 while (true){ 9 10 fun(); 11 } 12 13 } 14 15 public synchronized void fun(){ 16 if (num > 0){ 17 try { 18 Thread.sleep(10); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 System.out.println(Thread.currentThread().getName() + "得到當前數字爲:" + num); 23 num--; 24 } 25 } 26 }
demo05:
1 public class demo05 { 2 3 public static void main(String[] args) { 4 5 RunnableImpl runnable = new RunnableImpl(); 6 new Thread(runnable).start(); 7 new Thread(runnable).start(); 8 new Thread(runnable).start(); 9 10 } 11 }
3.靜態同步方法:
public static synchronized void method(){ 可能會產生線程安全的代碼,只能調用靜態屬性 }
RunnableImpl:靜態方法的鎖對象是本類的class屬性-->class文件對象
1 public class RunnableImpl implements Runnable { 2 3 private static int num = 100; //靜態屬性 4 5 @Override 6 public void run() { 7 8 while (true){ 9 10 fun(); 11 } 12 13 } 14 15 //靜態方法 16 public static synchronized void fun(){ 17 if (num > 0){ 18 try { 19 Thread.sleep(10); 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 System.out.println(Thread.currentThread().getName() + "得到當前數字爲:" + num); 24 num--; 25 } 26 } 27 }
另外一種寫法:
1 //靜態方法 2 public static void fun() { 3 synchronized (RunnableImpl.class) { 4 if (num > 0) { 5 try { 6 Thread.sleep(10); 7 } catch (InterruptedException e) { 8 e.printStackTrace(); 9 } 10 System.out.println(Thread.currentThread().getName() + "得到當前數字爲:" + num); 11 num--; 12 } 13 } 14 15 }
demo06:
1 public class demo06 { 2 3 public static void main(String[] args) { 4 5 RunnableImpl runnable = new RunnableImpl(); 6 new Thread(runnable).start(); 7 new Thread(runnable).start(); 8 new Thread(runnable).start(); 9 10 } 11 }
(2)Lock鎖
java.util.concurrent.locks.Lock 機制提供了比 synchronized 代碼塊和方法更加普遍的鎖定操做。
Lock鎖也稱爲同步鎖
- Lock是一個接口在1.5版本後出現
- public void lock():加同步鎖
- public void unlock():釋放同步鎖
- ReentrantLock實現了Lock接口
1 public class RunnableImpl implements Runnable { 2 3 private int num = 100; //靜態屬性 4 5 private final ReentrantLock lock = new ReentrantLock(); 6 7 @Override 8 public void run() { 9 10 while (true) { 11 12 //加鎖 13 lock.lock(); 14 15 if (num > 0) { 16 try { 17 Thread.sleep(10); 18 System.out.println(Thread.currentThread().getName() + "得到當前數字爲:" + num); 19 num--; 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } finally { 23 24 //釋放鎖 25 lock.unlock(); 26 } 27 } 28 } 29 } 30 }
(3)線程的狀態
- 初始(NEW):新建立了一個線程對象,但尚未調用start()方法。
- 運行(RUNNABLE):Java線程中將就緒(ready)和運行中(running)兩種狀態籠統的稱爲「運行」。線程對象建立後,其餘線程(好比main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取CPU的使用權,此時處於就緒狀態(ready)。就緒狀態的線程在得到CPU時間片後變爲運行中狀態(running)。
- 阻塞(BLOCKED):表示線程阻塞於鎖。
- 等待(WAITING):進入該狀態的線程須要等待其餘線程作出一些特定動做(通知或中斷)。
- 計時等待(TIMED_WAITING):該狀態不一樣於WAITING,它能夠在指定的時間後自行返回。
- 死亡(TERMINATED):表示該線程已經執行完畢。
(4)線程經常使用方法:
- void wait():當前線程進入等待狀態
- void wait(long timeout):當前線程進入等待狀態後,等待指定時間後自動喚醒
- void notify():喚醒等待的單個線程,隨機喚醒一個
- void notifyAll():喚醒等待的全部線程
(5)線程通訊案例:單生產者和單消費者問題
產品類:
1 public class Product { 2 3 private String name; 4 private Deque<String> deque; 5 6 7 public Product(String name, Deque<String> deque) { 8 this.name = name; 9 this.deque = deque; 10 } 11 12 public Deque<String> getDeque() { 13 return deque; 14 } 15 16 public void setDeque(Deque<String> deque) { 17 this.deque = deque; 18 } 19 20 public String getName() { 21 return name; 22 } 23 24 public void setName(String name) { 25 this.name = name; 26 } 27 28 29 @Override 30 public String toString() { 31 return "Product{" + 32 "name='" + name + '\'' + 33 ", deque=" + deque + 34 '}'; 35 } 36 }
單生產者Producer類:
1 public class Producer implements Runnable { 2 3 private Product product; 4 private int productID = 1; 5 6 public Producer(Product product, int productID) { 7 this.product = product; 8 this.productID = productID; 9 } 10 11 public Producer(Product product) { 12 this.product = product; 13 } 14 15 @Override 16 public void run() { 17 18 while (true) { 19 synchronized (product) { 20 21 //當前產品數量 22 int num = product.getDeque().size(); 23 24 if (num >= 10) { 25 26 try { 27 product.wait(); 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } 31 } 32 33 num = product.getDeque().size(); 34 35 String name = productID + "號產品"; 36 product.getDeque().add(name); 37 productID++; 38 39 System.out.println("+++++當前庫存" + num + "件產品,正在生成產品名爲" + name + "的產品"); 40 41 42 try { 43 Thread.sleep(100); 44 } catch (InterruptedException e) { 45 e.printStackTrace(); 46 } 47 48 49 50 System.out.println("++++++++++++++++已經生產好了產品名爲" + name + "的產品,當前庫存" + (num + 1) + "件產品"); 51 product.notify(); 52 53 } 54 } 55 } 56 }
單消費者Consumer類:
1 public class Consumer implements Runnable { 2 3 private String name; 4 private Product product; 5 6 public Consumer(String name, Product product) { 7 this.name = name; 8 this.product = product; 9 } 10 11 @Override 12 public void run() { 13 14 while (true) { 15 16 synchronized (product) { 17 18 19 int num = product.getDeque().size(); 20 21 if (num <= 0) { 22 try { 23 product.wait(); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 } 28 29 num = product.getDeque().size(); 30 31 String name = product.getDeque().getFirst(); 32 33 System.out.println("-----消費者" + this.name + "正在消費產品名爲" + name + "的產品"); 34 35 product.getDeque().remove(); 36 37 try { 38 Thread.sleep(50); 39 } catch (InterruptedException e) { 40 e.printStackTrace(); 41 } 42 43 System.out.println("--------------消費者" + this.name + "已經消費了產品名爲" + name + "的產品,剩餘庫存" + (num - 1)); 44 product.notify(); 45 46 47 } 48 } 49 } 50 }
測試代碼:
1 public class day01 { 2 3 public static void main(String[] args) { 4 5 Deque<String> deque = new ArrayDeque<>(); 6 Product product = new Product("產品類別1",deque); 7 8 new Thread(new Producer(product)).start(); 9 new Thread(new Consumer("Consumer1", product)).start(); 10 11 12 } 13 }
5.線程池
- JDK1.5以後提供的線程池
- 頂層接口java.util.concurrent.Executor做爲執行線程的工具而不是真正的線程池。
- java.util.concurrent.ExecutorService爲線程池接口繼承Executor接口。
- java.util.concurrent.Executors爲線程池的工廠類。
(1)線程池經常使用方法
- Executors工廠類中:
- public static ExecutorService new FixedThreadPool(int nThreads):建立一個能夠重用的固定線程數的線程池
- ExecutorService線程池接口:
-
Future<?> submit(Runnable task):提交一個Runnable任務執行。
- void shutdown():銷燬線程池。
-
(2)執行步驟:
- 使用Executors工廠類調用FixedThreadPool方法來建立一個線程池。
- 編寫一個實現Runnable接口的實現類,重寫run方法。
- 調用ExecutorService類方法submit傳入Runnable的實現類並執行。
- 最終銷燬線程池ExecutorService類方法shutdown,現實中不必定須要。
RunnableImpl:
1 public class RunnableImpl implements Runnable { 2 3 private int num = 2; 4 @Override 5 public void run() { 6 while (num-- > 0) { 7 8 System.out.println(Thread.currentThread().getName()); 9 try { 10 Thread.sleep(100); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 } 16 }
main:
1 public class demo01 { 2 3 public static void main(String[] args) { 4 5 //建立 6 ExecutorService es = Executors.newFixedThreadPool(10); 7 8 //執行 9 es.submit(new RunnableImpl()); //pool-1-thread-1 10 es.submit(new RunnableImpl()); //pool-1-thread-2 11 es.submit(new RunnableImpl()); //pool-1-thread-3 12 es.submit(new RunnableImpl()); //pool-1-thread-4 13 14 //銷燬 15 es.shutdown(); 16 17 } 18 }