您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“Java線程中關鍵字和方法的示例分析”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“Java線程中關鍵字和方法的示例分析”這篇文章吧。
代碼在寫入 volatile 修飾的變量的時候
改變線程工作內存中volatile變量副本的值
將改變后的副本的值從工作內存刷新到主內存
代碼在讀取 volatile 修飾的變量的時候
從主內存中讀取volatile變量的最新值到線程的工作內存中
從工作內存中讀取volatile變量的副本
static class Counter{ public int flag = 0; } public static void main(String[] args) { Counter counter = new Counter(); Thread t1 = new Thread(){ @Override public void run() { while (counter.flag == 0){ } System.out.println("循環結束"); } }; t1.start(); Thread t2 = new Thread(){ Scanner scanner = new Scanner(System.in); System.out.println("請輸入一個整數:"); counter.flag = scanner.nextInt(); t2.start();
預期的結果是:
線程1會先進入循環狀態,線程2讀取一個用戶輸入的整數。隨著用戶的輸入一個非0的整數之后,線程1就會終止。
實際效果:
線程2輸入完畢后,線程1循環并未結束
線程1的核心代碼中,循環其實啥也沒干,反復快速的執行循環條件中的比較操作。
先從內存中讀取flag的值到CPU中
在CPU中比較這個值和0的關系
編譯器判定這個邏輯中循環沒有干啥事,只是頻繁的讀取內存而已,于是編譯器就把讀內存的操作優化了,第一次把內存中的數據督讀到CPU之后,后序的內存并不是真正的從內存中讀,而是直接從剛在的CPU中讀數據
編譯器認為flag沒有改動,其實只是在當前線程中沒有改動,編譯器就不能感知到其他的線程對flag進行了修改
static class Counter{ public volatile int flag = 0; } public static void main(String[] args) { Counter counter = new Counter(); Thread t1 = new Thread(){ @Override public void run() { while (counter.flag == 0){ } System.out.println("循環結束"); } }; t1.start(); Thread t2 = new Thread(){ Scanner scanner = new Scanner(System.in); System.out.println("請輸入一個整數:"); counter.flag = scanner.nextInt(); t2.start();
加了volatile之后,對這個內存的讀取操作肯定是從內存中來讀
不加volatile的時候,讀取操作可能不是從內存中讀取,從CPU上讀取舊值,這都是不確定的
volatile 和 synchronized 有著本質的區別.
synchronized 能夠保證原子性, volatile 保證的是內存可見性
synchronized 既能保證原子性, 也能保證內存可見性.
由于線程之間是搶占式執行的, 因此線程之間執行的先后順序難以預知. 但是實際開發中有時候我們希望合理的協調多個線程之間的執行先后順序
wait 做的事情:
使當前執行代碼的線程進行等待. (把線程放到等待隊列中)
釋放當前的鎖
滿足一定條件時被喚醒, 重新嘗試獲取這個鎖.
wait 要搭配 synchronized 來使用. 脫離 synchronized 使用 wait 會直接拋出異常.
wait 結束等待的條件:
其他線程調用該對象的 notify 方法.
wait 等待時間超時 (wait 方法提供一個帶有 timeout 參數的版本, 來指定等待時間).
其他線程調用該等待線程的 interrupted 方法, 導致 wait 拋出 InterruptedException 異常.
public static void main(String[] args) throws InterruptedException { Object object = new Object(); synchronized (object){ System.out.println("等待前"); object.wait(); System.out.println("等待后"); } }
就相當于一個人去ATM上取錢,發現ATM中沒錢,然后阻塞等待(不參與后續鎖的競爭)——wait
等到銀行的工作人員來送錢,你可以取錢了——notify
notify 方法是喚醒等待的線程.
方法notify()也要在同步方法或同步塊中調用,該方法是用來通知那些可能等待該對象的對象鎖的其它線程,對其發出通知notify,并使它們重新獲取該對象的對象鎖。
如果有多個線程等待,則有線程調度器隨機挑選出一個呈 wait 狀態的線程。(并沒有 "先來后到")
在notify()方法后,當前線程不會馬上釋放該對象鎖,要等到執行notify()方法的線程將程序執行 完,也就是退出同步代碼塊之后才會釋放對象鎖。
public static void main(String[] args) { Object locker = new Object(); Thread t1 = new Thread(){ @Override public void run() { synchronized (locker){ while (true){ System.out.println("wait開始"); try { locker.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("wait結束"); } } } }; t1.start(); Thread t2 = new Thread(){ Scanner scanner = new Scanner(System.in); System.out.println("輸入一個整數,繼續執行"); int num = scanner.nextInt(); System.out.println("notify開始"); locker.notify(); System.out.println("notify結束"); t2.start(); }
notify方法只是喚醒某一個等待線程.
使用notifyAll方法可以一次喚醒所有的等待線程.
以上是“Java線程中關鍵字和方法的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。