概念
?
?
第一個多線程程序?
可以通過查看jdk路徑來找到jdk的控制
可以通過jconsole來查看線程。
創建線程
這是實現多線程的其中一種方法,繼承Thread類,實現run方法,之后實例化繼承了Thread類的MyThread方法,調用start方法,就會自動創建一個線程去執行run方法,如果直接調用run方法是不會創建新的線程的。
實現Runnable接口,也要實現run方法,但是和繼承Thread類的不同,耦合度較低,在實例化Thread時將實例化繼承了Runnable接口的對象傳遞,start執行的就是這個run方法
其他變形
多線程的優勢-增加運行速度
Thread類以及常見方法
Thread的常見構造方法
Thread的常見屬性
比較重要的就是判斷后臺線程和判斷是否存活的方法。前臺線程可以決定進程是否結束,只要還有一個前臺線程運行,進程就不會終止,后臺線程就算在運行,只要前臺線程都結束了,進程還是要結束。
啟動?個線程 - start()
終止線程
?
可以自定義一個變量,約定好在這個變量修改為約定值時,就終止線程。
Thread對象調用,該對象對應的線程終止。
在一些情況下,有要求,線程要有結束的先后順序,那么就可以通過線程的對象,來等待線程,來做到線程的結束順序變為可控。
獲取當前線程引用
這是一個靜態方法,可以直接通過類來調用,返回的時當前調用該方法的類對象的引用。
休眠線程
線程的狀態
觀察線程的所有狀態
Waiting是
線程狀態和轉移的意義
?
多線程帶來的的風險-線程安全 (重點)
觀察線程不安全
線程安全的概念
線程不安全的原因
可見性
指令重排序
解決之前的線程不安全問題
synchronized 關鍵字 - 監視器鎖 monitor lock
synchronized 的特性
不可重入的鎖在加了一個鎖之后,再次加同一把鎖就會出現死鎖,這種鎖就是不可重入鎖
而可重入鎖在再次上鎖的時候發現這個鎖是自身持有的鎖,那么就不會再加鎖,而鎖的釋放時根據第一次上該鎖的作用域來確定的。
synchronized 使用示例
這個鎖可以是任意的對象,一個對象就可以視為是一個鎖。
鎖this對象和直接修飾普通方法是一樣的。
死鎖
Java 標準庫中的線程安全類
volatile 關鍵字
volatile關鍵字可以將一些因為java優化所導致的可見性問題解決。
?
工作內存或寄存器和主內存
因為并不是所有的cpu都是直接優化在寄存器上的,所以直接說優化都是將數據存儲在寄存器上方便讀取不太合適,還有多級別的緩存,所以work memory更加合適。
wait 和 notify
wait()方法
notify()方法
notifyAll()方法
notifyAll雖然一次性喚醒了所有鎖,但是這些鎖還是需要重新競爭的。
wait和sleep的區別
多線程案例
單例模式
餓漢模式
懶漢模式(單線程)
懶漢模式-多線程版
懶漢模式-多線程版-改進
可能因為指令重排序導致還沒有申請內存空間就將值賦給了instance,導致其他線程直接帶著這個沒有初始化的變量返回了。
解決方法實際上還是volatile
阻塞隊列
一個線程給阻塞隊列添加數據,一個線程消費數據。
生產數據,阻塞隊列滿了,就會阻塞,消費數據,阻塞隊列為空就會阻塞。
這個判斷語句最好選擇while,本質上是為了二次驗證數據是否滿足要求,因為wait不僅僅能夠被notify喚醒,也可能是設置的時間到了被喚醒,這種情況就需要對參數進行再次校驗。
線程池
創建線程池的方法
參數代表的含義
工廠模式
工廠模式也是一種設計模式,主要應用再構造方法中,構造方法同名同參無法構成重載,因為構造方法要求是方法名與類名等同,而另外定義一個工廠類,提供構造對象的方法,就可以實現這些功能,而且可以根據不同的構造方法提供不同的參數。
因為原本的構造方法有些復雜
自主實現一個簡易的線程池
定時器
第一個參數也就是實現了run方法的runnable接口的子類。
實現一個簡易定時器
首先創建一個保存了執行方法和執行時間的類,并且要實現compareTo,因為需要加到優先級隊列里面。
第二部要創建一個定時器類。
schedule傳遞的參數是一個runnable類和一個時間,代表多久之后執行方法,實例化一個保存了執行時間和方法的類對象。保存的時間是時間戳,獲取系統當前時間并將多久之后執行加上,加入優先級隊列,喚醒在locker鎖之中阻塞的一個線程,讓線程去執行任務。
這里需要注意的是鎖的范圍,這里將新創建的MyTimeTask對象也包括進去了,是否需要包括進去,看的是需求,如果算調用方法開始算時間就在鎖外面,如果是鎖里面開始算時間就包括在鎖里面。
構造方法開始就要循環判斷是否有任務需要執行。
但是之前的方法有缺陷,會導致cpu資源被占用嚴重,所以在判斷到隊列為空和時間未到都會開始阻塞,而時間未到的阻塞還會另外設定一個時間,這個時間就是距離實際執行方法時間的差值,盡管線程被喚醒可能還是沒有到執行時間,因為是優先級隊列,喚醒的是最早執行的,那么也不過是再進去while循環做一次時間和隊列為空判斷,對于長時間循環而言,這些消耗微不足道。
這里并不適合使用sleep