第六週
Day26
異常
-
異常的概念
- 異常在運行過程中出現特殊的情況
- 異常處理的必要性:任何程序都可能存在大量的未知問題、錯誤;如果不對這些問題進行正確的處理,則可能導致程序的中斷,造成不必要的損失
-
異常的分類
- Throwable:可拋出的,一切錯誤或異常的父類,位於java.lang包中。
- -Error:JVM 、硬件、執行邏輯錯誤,不能手動處理
- Exception:程序在運行和配置中產生的問題,可處理
- RuntimeException:運行時異常,可處理,可不處理
- CheckedException:受查異常,必須處理
-
異常 的產生
- 自動拋出異常:當程序值在運行時遇到不規範的代碼或結果時,會產生異常
- 手動拋出異常:語法:throws new 異常類型(“實際參數”)
- 產生異常結果:相當於遇到return 語句。導致程序因異常而終止
-
異常的傳遞
- 概念:按照方法的調用鏈反向傳遞。如果始終沒有處理異常,最終會由JVM進行默認異常處理(打印堆棧跟蹤信息)
- 受查異常: throws 聲明異常吧,修飾在方法參數列表後端
- 運行時異常:因可處理可不處理。無需聲明異常
-
異常的處理
try {
可能出現異常的代碼
}catch(Exception e ){
異常處理的相關代碼,如 :getMessage()、printStackTrace()}finally{
無論是否出現異常,都需執行的代碼結構,常用於釋放資源
}
-
常見異常處理結構
- try{} catch {}
- try{} catch{}catch{}
- try{} catch{}finally{}
- try{}catch{}catch{] finally{}
- try{} finally
- 注意:多重catch,遵循從子(小)到父(大)的順序,父類異常在最後
-
-
自定義異常
-
需繼承自ERxception 或Exception 的子類,常用RuntimeException
-
必要提供的構造方法
- 無參數構造方法
- String message參數的構造方法
-
-
方法的覆蓋
-
帶有異常聲明的方法覆蓋
- 方法名、參數列表、返回值類型必須和父類相同
- 子類的訪問修飾符合父類相同或是比父類更寬
- 子類中的方法,不能拋出比父類更多、更寬的異常
-
-
總結
-
異常的概念:程序在運行過程中出現的特殊情況
-
異常的分類
- RuntimeException:運行時異常、可處理、可不處理
- CheckedException:受查異常,必須處理
-
異常的產生
- throw new 異常類型(“實際參數”)
- 相當於遇到return 語句,導致程序因異常而終止
-
異常的傳遞
- 按照方法的調用鏈反向傳遞,如始終沒有處理異常,最終會由jvm 進行默認異常處理(打印堆棧跟蹤信息)
-
異常的處理
- try{} catch{} finally{}
-
帶有異常聲明的方法覆蓋:
- 子類中的方法,不能拋出比父類更多、更寬的異常
-
Day27、Day28
多線程
-
線程的概念
- 運行時的程序,才被稱爲進程
- 線程,又輕量級進程。程序中的一個順序控制流程,同時也是CPU的基本調度單位。進程由多個線程組成,彼此間完成不同的工作,交替執行,稱爲多線程
-
線程的組成
-
任何一個線程都具有基本的組成部分
-
CPU時間片:操作系統會爲每個線程分配執行時間
-
運行數據:
- 堆空間:存儲線程需使用的對象,多個線程可以共享堆中的對象
- 棧空間:存儲線程需使用的局部變量,每個線程都擁有獨立的棧
-
邏輯代碼
-
-
創建線程的方式
-
創建線程的第一種方式
public class TestCreateThread {
public static void main(String[] args){
MyThread t1 = new MyThread();
t1.start();}
}
class MyThread extends Thread{
public void run(){
for (int i = 1;i<=50;i++){System.out.println("MyThread:L"+i);
}
}
}
-
創建線程的第二種方式:
public class TestCreateThread{
public static void main(String [] args){MyRunnable mr = new MyRunnable();
Thread t2 = new Thread(mr);
t2.start();}
}class MyRunnale implements Runnnale {
public void run (){ for (int i = 1;i<=50;i++){ System.out.println("MyRunnale:"+i);
}
}
}
-
-
線程的狀態
-
New 初始狀態
- 線程對象被創建,即爲初始狀態。只在堆中開闢內存,與常規對象無異
-
Ready就緒狀態
- 調用start()之後,進入就緒狀態。等待OS選中,並分配時間片
-
Running 運行狀態
- 獲得時間片之後,進入運行狀態,如果時間片到期,則返回到就緒狀態
-
Terminated終止狀態
- 主線程main()或獨立線程run()結束,進入終止狀態, 並釋放持有的時間片
-
-
常見方法
-
休眠
- public static void sleep(long millis)
- 當前線程主動休眠millis毫秒
-
放棄
- public static void yield
- 當前線程主動放棄時間片,回到就緒狀態你,競爭下一次時間片
-
結合
- public final void join()
- 允許其他線程加入到當前線程中
-
-
線程的安全
-
需求
- A線程將“HeLlo”存入數組的第一空位;B線程將“world”存入數組的第一個空位
-
線程不安全
- 當多線程併發訪問臨界資源時,如果破壞原子操作,可能會造成數據不一致
- 臨界資源:共享資源(同一對象),一次僅允許一個線程使用,纔可保證其正確性
- 原子操作:不可分割的多步操作,被視作一個整體,其順序和步驟不可打亂或缺省
-
-
同步方式
-
同步代碼塊
synchronized (臨界資源對象){//對臨界資源對象加鎖
// 代碼(原子操作)
}
-
注意
- 每個對象都有一個互斥鎖標的標記,用來分配給線程的
- 只有擁有對象互斥鎖標記的線程,才能進入對該對象加鎖的同步代碼塊
- 線程退出同步代碼塊時,會釋放相應的互斥鎖標記
-
-
同步方式2
-
同步方法
synchronized 返回值類型 方法名稱(形參列表()){// 對當前對象(this)加鎖
//代碼(原子操作)
}
-
注
- 只有擁有對象互斥鎖標記的線程,才能進入該對象加鎖的同步方法中
- 線程退出同步方法時,會釋放相應的互斥鎖標記
-
-
同步規則
-
注意
- 只有在調用包含同步代碼的方法,或者同步方法時,才需要對象的鎖標記
- 如調用不包含同步代碼塊的方法,或普通方法時,則不需要鎖標記,可直接調用
-
已知jdk 中線程安全的類
- StringBuffer
- Vector
- Hashtable
- 以上的類中的公開方法,均爲synchonized修飾的同步方法
-
-
經典問題
-
死鎖
- 當第一個線程擁有A對象鎖標記。並等待B對象鎖標記,同時第二個線程擁有B對象鎖標記,並等待 A 對象鎖標記時,產生死鎖
- 一個線程可以同時擁有多個對象的鎖標記,當線程阻塞時,不會釋放已經擁有的鎖標記,由此可能造成死鎖
-
生產者、消費者
- 若干個生產者在生產產品,這些產品將提供若干消費者去消費,爲了使生產者和消費者能併發執行,在兩者之間設置一個能存儲多個產品的緩衝區,生產者將生產的產品放入緩衝區中,消費者從緩衝區中取走產品進行消費,顯然生產者和消費者之間必須保持同步,即不允許消費者到一個空的緩衝區中取產品,也不允許生產者向一個慢的緩衝區中放入產品
-
-
線程通信
-
等待
- public final void wait()
- public final void wait(long timeout)
- 必須在對obj 加鎖的同步代碼塊中。在一個線程中,調用obj.wait() 時。此線程會釋放其擁有的所有鎖標記。同時此線程阻塞在o 的等待隊列中。釋放 鎖,進入等待隊列
-
通知
- public final void notify()
- public final void notifyAll()
- 必須在對obj 加鎖的同步代碼塊中。從obj 的Wating中釋放一個或全部線程。對自身沒有任何影響
-
-
總結
-
線程的創建
- 方式1:繼承Thread類
- 方式2:實現Runnable 接口(一個任務Task),傳入給Thread對象並執行
-
線程安全
- 同步代碼塊:爲方法中的局部代碼(原子操作)加鎖
- 同步方法:爲方法中的所有代碼(原子操作)加鎖
-
線程間的通信:
- wait(0 /wait(long timeout) 等待
- notify()/notifyAll():通知
-
-
線程池
-
現有問題
- 線程是寶貴的內存資源、單個線程約佔1MB空間,過分分配易造成內存溢出。
- 頻繁的創建及銷燬線程會增加虛擬機回收效率、資源開銷,造成程序性能下降
-
線程池
- 線程容器,可設定線程分配的數量上限
- 將預先創建的線程對象存入池中,並重用線程池中的線程對象
- 避免頻繁的創建和銷燬
-
-
線程池原理
- 將任務提交給線程池,由線程池分配線程、運行任務,並在當前任務結束後複用線程
-
獲取線程池
- 常用的線程池接口和類(所在包java.util.concurrent)
- Executor:線程池的頂級接口
- ExecutorService:線程池接口,可通過submit(Runnale task )提交任務代碼
- Executors工廠類:通過此類可以獲得此類可以獲得一個線程池
- 通過newFixedThreadPool(int nThreads)獲取固定數量的線程池。參數:指定線程池中線程的數量
- 通過newCachedThreadPool() 獲取動態數量的線程池,如不夠則創建新的,沒有上限
-
Callable接口
public interface Callable{
public V call () throws Exception;
}
- JDK5加入,與Runnale 接口類似,實現之後代表一個線程任務
- Callable具有泛型返回值、可以聲明異常
-
Future 接口
- 概念:異步接收ExecutorService.submit()所返回的狀態結果,當中包含了call() 返回值
- 方法: V get() 以阻塞形式等待Future中的異步處理結果(call() 的返回值)
-
線程的同步
- 形容一次方法調用,同步一旦開始。調用者必須等待該方法返回,才能繼續
- 注意:單條路徑
-
線程的異步
- 形容一次方法調用,異步一旦開始,像是一次消息傳遞,調用者告知之後立刻返回,二者競爭時間片,併發執行
- 注意:多條執行路徑
-
Lock接口
-
jdk5 加入,與synchronized 比較,顯示定義,結構更靈活
-
提供更多實用性方法,功能更強大、性能更優越
-
常用方法
- void lock() // 獲取鎖,如鎖被佔用。則等待
- boolean tryLock() //嘗試獲取鎖(成功返回true。失敗返回false ,不阻塞)
- void unlocck() // 釋放鎖
-
-
重入鎖
- ReentrantLock:Lock接口的實現類,與synchronized 一樣具有互斥鎖功能
-
讀寫鎖
-
ReentrantReadWriteLock
- 一種支持一寫多讀的同步鎖,讀寫分離,可分別分配讀鎖,寫鎖
- 支持多次分配讀鎖,使多個讀操作可以併發執行
-
互斥規則
-
寫-寫:
- 互斥,阻塞
-
讀-寫
- 互斥,讀阻塞寫,寫阻塞讀
-
讀-讀
- 不互斥、不阻塞
-
在讀操作遠遠高於寫操作的環境中,可保障線程安全的情況下,提高運行效率
-
-
day29,day30
<愛編碼的程序員> 這個微信公衆號有更多關於java知識的詳解
I/O 框架部分
-
流的概念
- 內存與存儲設備之間傳輸數據的通道
-
流的分類
-
按方向
-
輸入流:
- 將<存儲設備 >中的內容讀入到<內存>中
-
輸出流
- 將<內存>中的內容寫入到<存儲設備>中
-
-
按單位
-
字符流
- 以字符爲單位,只能讀寫文本數據
-
字節流
- 以字節爲單位,可以讀寫所有數據
-
-
按功能
-
節點流
- 具有實際傳輸數據的讀寫功能
-
過濾流
- 在節點流的基礎之上增強功能
-
-
-
字節流
-
字節流的父類(抽象類)
-
InputSteam:字節輸入流
- public int read()[]
- public int read(byte[] b){}
- public int read(byte[] b ,int off ,int len){}
-
outputStream:字節輸出流
- public void write(int n){}
- public void write(byte[] b ){}
- public void write(byte[] b ,int off,int len){}
-
-
編碼方法
-
字符流
-
File類