點擊查看:原文
本文是清華大學許斌老師的公開課:Java語言程序設計進階 的課堂筆記,快速複習一下,時間有限,因此大量直接截圖。許斌老師聲明:沒有配套講義,建議參考書籍:周志明《深入理解java虛擬機》。(JUC) java.utile.concurrency 部分參考源碼和技術博客。
第一章 線程(上)
1.0 導學
2.0 通過Thread類創建線程
3.0 線程的休眠
注:線程休眠的原因就是讓其他線程有執行的機會
1.4 Thread類詳解
注:線程啓動(即調用start方法)並不意味着線程馬上運行,線程是否運行取決於線程調度器。
1.5 通過Runnable接口創建線程
注:Runnable接口中我們所實現的run方法就是我們這個線程想要執行的代碼
1.6 線程內部的數據共享
同樣一個線程類,它可以實例化出很多線程。同樣一個線程,它們是可以共享它們的代碼和數據,那也就是說當我們實現了Runnable接口的這個類,它所實例出來的對象的話,它去構造出的線程,它們之間是可以共享它們的代碼和它們之間的一些數據的。
小結
第二章 線程(中)
2.0 導學
2.1 線程同步的思路
注:那原因就是在於說這兩個線程的話,它們是同一優先級,只不過是說這個producer先這個排在前面,所以的話從調度上,往往會調度它這個producer先執行,那它一執行呢就把這個票都生產完了,然後再等待着賣票的程序把它去賣掉,這是一種有意思的這個現象
2.2 線程同步的實現方式—Synchronization
注:把這兩行代碼變成一個(原子)操作,就是在執行過程中不可能被打散執行
注:用synchronized後面大括號括起來其實是代碼,實際上它把它變成一個原子操作,也就是說當我拿到這個對象t的鎖的時候,我這裏面的這些代碼是肯定都會被執行的,不會說我執行某一句以後就被這個打斷,然後那個插入別的線程去執行去訪問這個對象t,所以這個是synchronized它的很重要的作用。
就像剛纔我們那個例子:我們在這個售票線程裏面,每售出來票的時候,它就會休眠一毫秒,但休眠一毫秒的時候,它不會釋放出它所佔有的這個ticket對象的鎖的,它一直會持有,所以這是一個獨特的一個地方。
2.3 線程的等待與喚醒
注:那現在wait notify notifyAll方法這三個方法都屬於object這個類的方法,也就意味着我們java當中所有的類它都有這個三個方法
注:修改之後,相當於票箱大小爲1,Tickets.size = 1。Tickets.put() 方法中的 notify() 與 Tickets.sell() 方法中的wait()一一對應,Tickets.put() 方法中的 wait() 與 Tickets.sell() 方法中的notify()一一對應。
2.4 後臺進程
2.5 線程的生命週期與死鎖
注:線程進入就緒狀態就是runnable state,即可運行狀態,但是並未開始運行,所以不是運行狀態(running state),是否運行取決於線程調度器是否調度它。(Runnable state isn’t running state)。
2.6 線程的調度
注:可以通過這個使用這個yield的方法來去稍微改變一下它的這個執行過程,yield方法主要作用是把自己當前運行的線程暫停下來,把線程讓給同優先級的線程執行,當如果這時候不存在同優先級的線程,那還是繼續執行當前運行的線程。
注:有交錯執行的這個過程,重要的原因:線程調用sleep方法,sleep方法是說我自己進入休眠,線程調度器有可能調度低優先級的這個線程,也就是說對高優先級的這個線程,如果要讓出自己的執行權限的話,就要調用sleep方法,然後給其它低優先級線程機會。如果高優先級的線程,僅僅只是調用了yield方法,它並不能給我們低優先級線程以執行的機會,它只給了它同優先級的線程以執行的這個機會。
小結
第三章 線程(下)線程安全與鎖優化
3.0 導學
注:它是想描述線程的安全,而最重要的是描述你的程序,甚至你是某個類它的線程安全的特性。
3.1 線程安全與線程兼容與對立
向大家展示一下,一些java API中類,它在碰到這個線程操作的時候,有可能產生線程出錯的這個情況。
運行過程當中它不經常出錯,但是偶爾也會出錯,出現了數組下標越界的錯誤。最重要的原因:剛纔有兩個線程Thread remove,Thread print這兩個線程都在同時訪問一個數據:vector,其中一個線程的操作:刪除我們相量中的元素,另外一個線程的操作讀取我們相量中的元素。大家看到這其實這兩個操作:是有點互逆的 互斥的,那在這個讀寫向量的過程當中就可能產生錯誤,那從我們發現了這個運行的結果當中也發現了這一點。
3.2 線程的安全實現-互斥同步
3.3 線程的安全實現-非阻塞同步
那其實大家也可以這麼理解,也就是說我們對於這種線程安全,就是對我們這個訪問對象的線程安全的這種控制不是放到我們當前count這個類,它的increment方法來去實現的而是已經放到底下的叫 Atomiclnteger 這個類來實現了,所以你就可以直接去調用它的這個方法來去實現加1的這個功能,那整體上我們這個新的類,class Counter就是這個類通過用 Atomiclnteger 來改進這類,它也是線程安全的,整體上也是線程安全的,只不過說當你寫這個類的時候,你不需要考慮自己去加上synchronized這樣的同步互斥的這種實現方式,而是通過直接使用了Atomiclnteger這樣一個本身就是線程安全的這個類,就能夠保證你的整個這個代碼達到線程安全的目的。
3.4 線程的安全實現-無同步方案
注:Threadlocal是我們java當中的一個類,它是存在於java.lang這個默認這個包當中。
這個 SequenceNumber 的實例,通過用 ThreadLocal 的這個方式也能夠保證這三個線程來訪問同一數據的時候,沒有產生錯誤。這也是爲什麼說,可以通過這個 ThreadLocal 就來去達到這個同步,就是說安全的這個目的。也不一定非得加個synchronize,因爲如果一旦加了synchronize的話,性能可能會受到影響,如果能通過類似 ThreadLocal 這樣這種線程的本地存儲的方式來達到我們這個對於數據訪問安全的控制化,那就能提高這個程序代碼的性能。
3.5 鎖優化
操作系統的堆棧與數據結構中堆棧概念參考:
- 什麼是堆?什麼是棧?他們之間有什麼區別和聯繫? - 知乎
https://www.zhihu.com/question/19729973 - https://jingyan.baidu.com/article/6c67b1d6a09f9a2786bb1e4a.html
注:由於細鎖太多,然後不斷切換線程的開銷反而降低了性能。
小結
- 線程安全指的是我們的訪問對象無論被多少個線程進行訪問都能夠保證我們這對象訪問的正確性,那與之相關的這個概念是線程兼容。
- 線程兼容是指我們的對象本身不是線程安全的,但是通過我們外部的同步控制能夠達到線程安全的目的.
- 線程對立指的是我們的訪問對象,它本身不是線程安全,那我們外部即使加上了同步的控制也不能保證這個對象的這個正確性。
- 我們還學習實現線程安全的幾種方式
- 首先是互斥同步
- 其次是非阻塞同步
- 無同步方案
- 最後我們還學習了鎖優化,那鎖優化的目標就是在我們不得不給我們的代碼加鎖的情況下如何去提高鎖的效率,進而達到提升整個代碼的效率的目標。
第四章 網絡編程(上)
4.0 導學
4.1 URL對象
注:保留端口號是計算機系統進行網絡交互需要的端口號,自己編寫程序不要去佔用這些保留端口號,具體保留端口號對應網絡服務google一下。
4.2 URLConnection對象
4.3 Get請求與Post請求
4.4 Socket通信原理
4.5 Socket通信實現
accept這個方法屬於ServerSocket方法,這種方法我們稱之它爲阻塞方法,就是說它是在那裏運行一直等着有客戶端來給它發送Socket連接請求,如果沒有客戶端給我們的服務端發送這個Socket連接請求accept就一直在那裏循環執行一直不返回,一直等到有客戶端的Socket發連接請求過來。那我們這服務端的 ServerSocket 這個對象的話它就會accept方法就會返回一個值,返回的是一個Socket對象,而這個Socket對象就是和我們客戶端的Socket對象進行對應的。
注:那需要提醒大家注意是我們這個程序非常簡單,簡單到什麼程度呢?就是說聊天的時候,都是你說一句 我說一句如果一個想連續說兩句話的話可能現有這個機制還處理不過來,必須是一人一句,當然我們同學可以把這個程序再進一步改進使它更加的豐富。
小結
第五章 網絡編程(下)
5.0 導學
5.1 Socket 多客戶端通信實現
注:先運行server線程,再運行client。
5.2 數據報通信
one-liners.txt這個是構造了一個文件輸入流,因爲我們做了一個非常簡單的模擬,也就是說把一些股票信息就寫到這個文件裏面了,寫到這個文件裏面了以後就是每次有客戶端發過來請求,說諮詢一下股票的價格的時候,我們就從這個文件裏讀出某一股票的價格在返還給我們的客戶端。
那剛纔這個程序當中客戶端服務端各一個程序,客戶端和服務端之間的通訊是通過數據報這個Socket來進行通訊的然後整個過程就非常類似於,我們人類進行平信這個通訊方式,也就是說客戶端通過構造一個DatagramPacket這個對象向它寫一封信,然後通過DatagramSocket的send方法把它發出去了,服務端收到了這封來信以後,通過這個來信知道了客戶端的地址和端口號,然後服務端它自己也寫一封信,說白了寫信就是構造一個DatagramPacket對象,寫好了以後,通過DatagramSocket的這個對象的send方法,把這個信再發出去又發還給客戶端,所以這個數據報包總結起來就非常類似於我們人類寫平信的這個過程。
5.3 使用數據報進行廣播通信
5.4 網絡聊天程序
那整個這個佈局其實大家採用一個BorderLayout就可以達到你的目標,那就是在BorderLayout的center中間那區域先放一個滾動面板,然後接着再放一個TextArea,然後在它的南部區域,我們先放一個Panel,緊接着再放一個TextView文本輸入區域,然後接着再放一個Button而且TextView和Button的話都是按照FlowLayout這種放置規則。
大家需要這個寫的事件響應是什麼呢?其實首先最重要的是說我們能夠接收這個在文本區域,就是最下面這個文本區域這個輸入的文本,我們可以給TextView這個組件來註冊一個監聽器,當我們這個一回車就在TextView裏面一輸入字符一回車的時候,它產生的是一個ActionEvent,所以我們可以給TextView註冊一個EventListener。那TextView旁邊的話,是一個按鈕發送,其實發送的話它所對應的這個事件處理也是ActionEvent,所以在這個例子當中我們只需要寫一個事件處理類然後都分別這個授權來去處理TextView和我們按鈕的這麼這個事件處理就可以完成獲得我們這個文本的這麼一個過程以及把它發送的一個過程。那在這裏面怎麼去獲得內容呢?TextView裏面有個一個getText的方法,那我們只要在我們的ActionPerform方法裏面去通過TextView的getText來獲得它的內容然後來決定一個是往外發送同時把它顯示到當前我們的這個界面上面。
小結
第六章 java虛擬機
6.0 導學
6.1 Java虛擬機概念
6.2 Java虛擬機內存劃分
本地方法(native method)它不一定是拿Java語言來編寫的方法,Java虛擬機是會運行在不同的操作系統和硬件上面,那在本身Java虛擬機的內部實現的時候,也會有一部分代碼是運行的是本地代碼非Java這個代碼,包括你們自己寫程序的時候,也可以比如用C或C加加,寫一段程序,最後把它嵌入到Java代碼當中這也是可以的。這個本地方法棧主要是用來執行本地方法,它同樣有可能會拋出異常,那所謂的拋出異常的種類也和虛擬機棧一樣,而我們的虛擬機棧它的對應的功能主要是用來執行我們的Java方法。
注:官網G1垃圾回收器介紹Getting Started with the G1 Garbage Collector
6.3 Java虛擬機類加載機制
比較舊的一些這個編程語言的經驗,當我們編譯完了以後可能還要做鏈接然後再執行,Java它有一個這個大的特點就是在程序運行過程當中來進行這個類的加載和連接,這樣的話就保證了,它這個一個程序運行的這個流暢和靈活性。
6.4 判斷對象是否存活算法及對象引用
通過sf.get方法可以獲取到這個對象,當然當這個對象被標誌爲需要回收的對象時,它就會返回的是空,所以說軟引用主要用戶來實現類似緩存的功能,在內存足夠的情況下,我們可以直接通過軟引用來取值而不需要從繁忙的真實來源去查詢數據提升速度,那當內存不足的時候就會自動刪除這部分的緩存數據,然後從真正的數據來源當中去查詢這些數據。
6.5 分代垃圾回收
注
- 將引用對象設置爲空,這種方式來去釋放內存的話,應該沒什麼大問題,但如果我們用system gt方法去釋放內存的話會大大的影響我們的系統性能。
- 不可達含義:不用了,沒有引用鏈指向。看英文:unavailable 就明白了,沒有引用指向它們而且畢竟不屬於空閒區,當然就不可使用。
6.6 典型的垃圾收集算法
6.7典型的垃圾收集器
注:jvm垃圾回收詳解參考官網G1垃圾回收器介紹Getting Started with the G1 Garbage Collector
今天介紹這幾種垃圾收集器一般說來它會影響到程序執行性能,尤其是當想編一些對效率要求非常高的Java程序的時候,比如說服務器端的Java程序的時候,有時候你會比較顧及Java虛擬機的垃圾回收效率是不是足夠那個幫助你的程序的運行,那據我所知國內外都有一些大公司在他們的服務器端性質當中重新改寫了一些關於Java虛擬機的裏面的垃圾回收的機制。
就舉一個例子來說:雙十一淘寶它的這個系統肯定就會承受着極大的這個用戶購買商品的點擊的壓力,淘寶實際上它的很多後臺系統拿是拿Java寫的,所以爲了提高這個Java在服務器端的這種工作效率,那我聽說他們也是對於一些垃圾回收的這些機制進行了改進。在一些性能要求特別高的情況下的話,可能我們在服務器端會對這些Java虛擬機以及相應它的一些局部做一些改進。
第七章 深入集合collection
7.0 導學
7.1 集合框架與ArrayList
7.2 LinkedList
7.3 HashMap與HashTable
可以看到這個HashMap底層數組的長度它總是2的N次方,這就是爲了保證數組的使用率最高,儘可能的減少這個碰撞現象的產生,當HashMap中的元素越來越多的時候,這個哈希衝撞的衝突的可能性也就越來越高,因爲數組的長度是固定的。那爲了提高這查詢的效率就要對這個HashMap的數組進行擴容容量變爲原來的兩倍,這時候,原數組當中的數據必須重新計算它在新數組當中的位置,並且放進去,那這個過程呢就非常耗時。當HashMap中的元素個數超過數組大小(取個名字叫lot fat),就會進行數組的擴容,這個(lot fat)的默認值爲0.75。那這個是一個非常消耗性能的操作所以如果我們已經預知這個HashMap當中元素的個數,那麼就能夠有效的提高HashMap的性能,所以這也是一個HashMap它的一個獨特的地方。
注:關於hashtable與hashmap 建議參考源碼解析 Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析
7.4 TreeMap與LinkedHashMap
所以說它和之前的那個HashMap的區別就是:HashMap裏面的數據結構第一級結構是一個數組,第二級結構是一個單向鏈表,而我們的LinkedHashMap第二級結構是一個雙向鏈表,所以在往裏添加的時候就可以根據你要添加元素的位置來決定是從正向的去檢索往裏添加,還是反向從隊尾開始去檢索進行添加。
7.5 HashSet
小結
-
常用的一些集合類
首先介紹了List Map Set 這幾個接口,那在這幾個接口之下又有好多的類是實現了這些接口,比如說我們的HashSet、ArrayList linkedList、HashMap、TreeMap等等這些。
-
集合類的內部的實現過程
爲什麼要介紹這些呢?是因爲要告訴大家說,它的每一個數據結構這些類它到底是怎麼實現的,你明白了它的實現原理了以後,你就知道說它的效率和性能到底是怎樣的,爲什麼高爲什麼低,哪些類到底和線程安全有沒有關係,有沒有已經實現的線程安全特性,那沒有實現的話,你就得自己通過加 synchronize 辦法來去實現,所以對於通過了解我們集合類的內部,你就可以很好的去運用這些集合類
-
各個集合類的適用範圍
由於這些集合類它本身所具有的特點並不一樣,所以當我們在編程序過程中,考慮選擇哪個類作爲我們的數據結構的時候,你就能夠很好的去選擇和決定。
第八章 反射與代理機制
8.0 導學
8.1 Java反射機制
8.2 Java靜態代理
8.3 Java動態代理
這個例子實際上是告訴大家說你可以給一個真實的對象,你給它生成一個動態代理,那生成這個動態代理的話。既然是個代理,你可以在這個真實對象的方法執行之前先做一些預處理,執行之後你還可以做一些後處理,所以你就可以增加一些你想幹的這個事情,而通過這個動態代理的話,它的好處就是能夠讓你更加方便的去實現這些代理的過程。
8.4 Java 反射擴展-jvm加載類原理
JAVA虛擬機中類加載的原理,什麼叫類加載,想想當編輯完JAVA的源程序以後,一編譯會得到是一大堆點class文件,那點class文件就是這些類文件,而這些類文件平常是存在硬盤上,也就存在電腦的文件系統當中那當這些類文件需要執行的時候就需要JAVA虛擬機把它從硬盤上給挪到我們內存當中,那整個挪到JAVA虛擬機內存當中過程實際上就是一個類加載的過程。
- 第二步要做連接,連接裏面也會包括第一部分是驗證、注意驗證主要驗證說,你裝載進來的這個點class文件它是不是符合我們JAVA虛擬機對於自解碼文件的一個規範,做格式的這種校驗,甚至是不是有惡意是不是有危害,這些都是我們驗證的過程,那第二個小步驟是準備要把我們這些類它的一些相應的靜態的成員做一下內存的分配,那第三小步驟的話是要解析,解析是什麼就是把我們很多的這些符號性的引用把它轉化成一種直接的引用。
- 第三個步驟是做類的初始化,比如說我們將類的靜態變量給它做賦於正確的初始值,注意這個初始值是指的是程序員在給它定義的這個初始值而不是說默認初始值,默認初始值這個確定是在第二個步驟連接步驟裏邊,這個準備小步驟裏邊已經實現了。
小結
我們今天主要講了三個方面的內容
-
JAVA的反射機制
JAVA反射機制爲程序員提供了一種直接去獲取類以及對象它的方法以及它的成員變量的一種方式,通過反射機制可以去通過一個字符串的名字去創建一個類的對象,並且很靈活的去調動它的所有的方法。
-
JAVA的代理機制
介紹了靜態代理和動態代理,之所以有JAVA代理機制是因爲說有些情況下並不想或者不能夠去直接訪問目標對象而需要中間有一箇中介的渠道,那這中介渠道就可以幫助很好的去控制和訪問目標對象。並且在訪問目標的前和後都可以增加一些預處理或者後處理。介紹了靜態代理的方式和動態代理的方式,動態代理方式會給大家很大的一個方便和靈活性
-
類的加載機制
把我們所有編譯好的點Class文件把它加載到我們的JAVA虛擬機當中來進行運行,那這個類加載過程當中,它實際上對於JAVA是一個動態的過程而且是一個可以從多個源頭進行加載的這個過程,理解的加載類的加載過程,對於大家今後編寫更加高效有效的JAVA程序會帶來很大的幫助。
參考:
- 周志明《深入理解java虛擬機》
- Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析
- Java Concurrency in Practice