Java線程安全狀態(tài)專題解析
一、觀察線程的所有狀態(tài)
線程的狀態(tài)是一個枚舉類型 Thread.State
public static void main(String[] args) { for (Thread.State state : Thread.State.values()){ System.out.println(state); } }
NEW: 安排了工作, 還未開始行動
RUNNABLE: 可工作的. 又可以分成正在工作中和即將開始工作.就緒狀態(tài)
BLOCKED: 這幾個都表示排隊等著其他事情
WAITING: 這幾個都表示排隊等著其他事情
TIMED_WAITING: 這幾個都表示排隊等著其他事情
TERMINATED: 工作完成了.
二、線程狀態(tài)和狀態(tài)轉移的意義
NEW:Thread對象有了,但是PCB還沒有
RUNNABLE:線程正在CPU上執(zhí)行或者即將到CPU上執(zhí)行(PCB在就緒隊列中,隨時可能被調(diào)度到)
WAITING:wait方法導致
TIMED_WAITING:sleep方法導致
BLOCKED:等待鎖導致
TERMINATED:對象還在,但PCB已經(jīng)沒了
public static void main(String[] args) { Thread t = new Thread(){ @Override public void run() { for (int i = 0; i < 100_00; i++){ } } }; System.out.println("線程啟動前:" + t.getState()); t.start(); while (t.isAlive()){ System.out.println("線程運行中:" + t.getState()); } System.out.println("線程結束后:" + t.getState()); }
三、多線程帶來的風險
線程不安全的原因
①線程是搶占式執(zhí)行的
線程不安全的萬惡之源,線程之間的調(diào)度完全由內(nèi)核負責,用戶代碼中感知不到,也無法控制。線程之間誰先執(zhí)行,誰后執(zhí)行,誰執(zhí)行到哪里從CPU上下來,這樣的過程用戶無法控制也無法感知到的。
②自增操作不是原子的
每次++都能拆成三個步驟
? ? ? ? 把內(nèi)存中的數(shù)據(jù)讀取到CPU中
? ? ? ? 把CPU中的數(shù)據(jù)+1
? ? ? ? 把計算的數(shù)據(jù)寫回內(nèi)存中
如果兩個線程串行執(zhí)行,此時計算結果為2。
如果兩個線程并行執(zhí)行,線程1進行++操作到一半的時候,線程也進行了++操作,此時自增兩次,但結果為1。
必須保證線程1save結束了,線程2再load,此時計算結果才正確
③多個線程嘗試修改同一個變量
如果是一個線程修改一個變量,線程安全
如果多個線程讀取同一個變量,線程安全
如果多個線程修改不同的變量。線程安全
④內(nèi)存可見性導致線程安全問題
⑤指令重排序
Java的編譯器在編譯代碼時,會對指令進行優(yōu)化,調(diào)整指令的先后順序,保證原有的邏輯不變的情況下,提高程序的運行效率
四,解決線程安全問題
鎖-synchronized
未加鎖
static class Counter{ public int count = 0; public void increase(){ count++; } } public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); Thread t1 = new Thread(){ @Override public void run() { for (int i = 0; i < 50000; i++){ counter.increase(); } } }; Thread t2 = new Thread(){ @Override public void run() { for (int i = 0; i < 50000; i++){ counter.increase(); } } }; t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(counter.count); }
?已加鎖
static class Counter{ public int count = 0; synchronized public void increase(){ count++; } } public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); Thread t1 = new Thread(){ @Override public void run() { for (int i = 0; i < 50000; i++){ counter.increase(); } } }; Thread t2 = new Thread(){ @Override public void run() { for (int i = 0; i < 50000; i++){ counter.increase(); } } }; t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(counter.count); }
此處的synchronized就是針對counter這個對象來加鎖,進入increase方法內(nèi)部,就把加鎖狀態(tài)設為ture,increase方法退出之后,就把加鎖狀態(tài)設為false,如果某個線程已經(jīng)把加鎖狀態(tài)設為ture,此處的其他的線程嘗試去加鎖,就會阻塞
synchronized的特性——刷新內(nèi)存
synchronized 的工作過程:
????????1. 獲得互斥鎖
????????2. 從主內(nèi)存拷貝變量的最新副本到工作的內(nèi)存
????????3. 執(zhí)行代碼
????????4. 將更改后的共享變量的值刷新到主內(nèi)存
????????5. 釋放互斥鎖
synchronized的特性——互斥
public static void main(String[] args) { Object locker = new Object(); Thread t1 = new Thread(){ @Override public void run() { Scanner scanner = new Scanner(System.in); synchronized (locker) { System.out.println("輸入一個整數(shù)"); int num = scanner.nextInt(); System.out.println("num= " + num); } } }; t1.start(); Thread t2 = new Thread(){ @Override public void run() { while (true){ synchronized (locker){ System.out.println("線程2獲取到鎖"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; t2.start(); }
一旦線程一獲取到鎖,并且沒有釋放的話,線程2就會一直在鎖這里阻塞等待
public static void main(String[] args) { Object locker1 = new Object(); Object locker2 = new Object(); Thread t1 = new Thread(){ @Override public void run() { Scanner scanner = new Scanner(System.in); synchronized (locker1) { System.out.println("輸入一個整數(shù)"); int num = scanner.nextInt(); System.out.println("num= " + num); } } }; t1.start(); Thread t2 = new Thread(){ @Override public void run() { while (true){ synchronized (locker2){ System.out.println("線程2獲取到鎖"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; t2.start(); }
?不是同一把鎖,就不回出現(xiàn)競爭,就沒有互斥了。
public static void main(String[] args) { Object locker1 = new Object(); Object locker2 = new Object(); Thread t1 = new Thread(){ @Override public void run() { Scanner scanner = new Scanner(System.in); synchronized (locker1.getClass()) { System.out.println("輸入一個整數(shù)"); int num = scanner.nextInt(); System.out.println("num= " + num); } } }; t1.start(); Thread t2 = new Thread(){ @Override public void run() { while (true){ synchronized (locker2.getClass()){ System.out.println("線程2獲取到鎖"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; t2.start(); }
這個代碼中,兩個線程都在針對locker1和locker2的類對象進行競爭,此處的locker1和locker2的類型都是Object,對應的對象都是相同的對象。?
到此這篇關于Java線程安全狀態(tài)專題解析的文章就介紹到這了,更多相關Java 線程安全內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
@valid 無法觸發(fā)BindingResult的解決
這篇文章主要介紹了@valid 無法觸發(fā)BindingResult的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12Hibernate連接三種數(shù)據(jù)庫的配置文件
今天小編就為大家分享一篇關于Hibernate連接三種數(shù)據(jù)庫的配置文件,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03SpringBoot詳解如果通過@Value注解給靜態(tài)變量注入值
這篇文章主要介紹了springboot如何通過@Value給靜態(tài)變量注入值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06高分面試從Hotspot源碼層面剖析java多態(tài)實現(xiàn)原理
這篇文章主要為大家介紹了在面試中從Hotspot源碼層面來剖析java多態(tài)的實現(xiàn)原理,這樣回答薪資隨你開,有需要的朋友可以借鑒參考下,希望大家多多加薪2022-01-01