日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java多线程编程核心技术 pdf_Java多线程编程核心技术之volatile关键字

發布時間:2023/12/19 java 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java多线程编程核心技术 pdf_Java多线程编程核心技术之volatile关键字 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

私信我或關注公眾號猿來如此呀,回復:學習,獲取免費學習資源包

volatile關鍵字

關鍵字volatile的主要作用是使變量在多個線程間可見。

1 關鍵字volatile與死循環

如果不是在多繼承的情況下,使用繼承Thread類和實現Runnable接口在取得程序運行的結果上并沒有多大的區別。如果一旦出現”多繼承“的情況,則用實現Runable接口的方式來處理多線程的問題就是很有必要的。

public class PrintString implements Runnable{ private boolean isContinuePrint = true; @Override public void run() { while (isContinuePrint){ System.out.println("Thread: "+Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public boolean isContinuePrint() { return isContinuePrint; } public void setContinuePrint(boolean continuePrint) { isContinuePrint = continuePrint; } public static void main(String[] args) throws InterruptedException { PrintString printString = new PrintString(); Thread thread = new Thread(printString,"Thread-A"); thread.start(); Thread.sleep(100); System.out.println("我要停止它!" + Thread.currentThread().getName()); printString.setContinuePrint(false); }}

運行結果:

Thread: Thread-A我要停止它!main

上面的代碼運行起來沒毛病,但是一旦運行在 -server服務器模式中64bit的JVM上時,會出現死循環。解決的辦法是使用volatile關鍵字。

關鍵字volatile的作用是強制從公共堆棧中取得變量的值,而不是從線程私有數據棧中取得變量的值。

2 解決異步死循環

在研究volatile關鍵字之前先來做一個測試用例,代碼如下:

public class PrintString implements Runnable{ private boolean isRunnning = true; @Override public void run() { System.out.println("Thread begin: "+Thread.currentThread().getName()); while (isRunnning == true){ } System.out.println("Thread end: "+Thread.currentThread().getName()); } public boolean isRunnning() { return isRunnning; } public void setRunnning(boolean runnning) { isRunnning = runnning; } public static void main(String[] args) throws InterruptedException { PrintString printString = new PrintString(); Thread thread = new Thread(printString,"Thread-A"); thread.start(); Thread.sleep(1000); printString.setRunnning(false); System.out.println("我要停止它!" + Thread.currentThread().getName()); }}

JVM有Client和Server兩種模式,我們可以通過運行:java -version來查看jvm默認工作在什么模式。我們在IDE中把JVM設置為在Server服務器的環境中,具體操作只需配置運行參數為 -server。然后啟動程序,打印結果:

Thread begin: Thread-A我要停止它!main

代碼 System.out.println("Thread end: "+Thread.currentThread().getName());從未被執行。

是什么樣的原因造成將JVM設置為-server就出現死循環呢?

在啟動thread線程時,變量boolean isContinuePrint = true;存在于公共堆棧及線程的私有堆棧中。在JVM設置為-server模式時為了線程運行的效率,線程一直在私有堆棧中取得isRunning的值是true。而代碼thread.setRunning(false);雖然被執行,更新的卻是公共堆棧中的isRunning變量值false,所以一直就是死循環的狀態。內存結構圖:

這個問題其實就是私有堆棧中的值和公共堆棧中的值不同步造成的。解決這樣的問題就要使用volatile關鍵字了,它主要的作用就是當線程訪問isRunning這個變量時,強制性從公共堆棧中進行取值。

將代碼更改如下:

volatile private boolean isRunnning = true;

再次運行:

Thread begin: Thread-A我要停止它!mainThread end: Thread-A

通過使用volatile關鍵字,強制的從公共內存中讀取變量的值,內存結構如圖所示:

使用volatile關鍵字增加了實例變量在多個線程之間的可見性。但volatile關鍵字最致命的缺點是不支持原子性。

下面將關鍵字synchronized和volatile進行一下比較:

  • 關鍵字volatile是線程同步的輕量級實現,所以volatile性能肯定比synchronized要好,并且volatile只能修飾于變量,而synchronized可以修飾方法,以及代碼塊。隨著JDK新版本的發布,synchronized關鍵字在執行效率上得到很大提升,在開發中使用synchronized關鍵字的比率還是比較大的。
  • 多線程訪問volatile不會發生阻塞,而synchronized會出現阻塞。
  • volatile能保證數據的可見性,但不能保證原子性;而synchronized可以保證原子性,也可以間接保證可見性,因為它會將私有內存和公共內存中的數據做同步。
  • 再次重申一下,關鍵字volatile解決的是變量在多個線程之間的可見性;而synchronized關鍵字解決的是多個線程之間訪問資源的同步性。
  • 線程安全包含原子性和可見性兩個方面,Java的同步機制都是圍繞這兩個方面來確保線程安全的。

    3 volatile非原子性的特征

    關鍵字雖然增加了實例變量在多個線程之間的可見性,但它卻不具備同步性,那么也就不具備原子性。

    示例代碼:

    public class MyThread extends Thread { volatile private static int count; @Override public void run() { addCount(); } private void addCount() { for (int i = 0;i<100;i++){ count++; } System.out.println(count); } public static void main(String[] args) { MyThread[] myThreads = new MyThread[100]; for (int i=0;i<100;i++){ myThreads[i] = new MyThread(); } for (int i=0;i<100;i++){ myThreads[i].start(); } }}

    運行結果:

    ...825383538153805378757675

    在addCount方法上加入synchronized同步關鍵字與static關鍵字,達到同步的效果。

    再次運行結果:

    ....960097009800990010000

    關鍵字volatile提示線程每次從共享內存中讀取變量,而不是從私有內存中讀取,這樣就保證了同步數據的可見性。但在這里需要注意的是:如果修改實例變量中的數據,比如i++,也就是比

    i=i+1,則這樣的操作其實并不是一個原子操作,也就是非線程安全。表達式i++的操作步驟分解為下面三步:

  • 從內存中取i的值;
  • 計算i的值;
  • 將i值寫入到內存中。
  • 假如在第二步計算i值的時候,另外一個線程也修改i的值,那么這個時候就會臟數據。解決的方法其實就是使用synchronized關鍵字。所以說volatile關鍵字本身并不處理數據的原子性,而是強制對數據的讀寫及時影響到主內存中。

    4 使用原子類進行i++操作

    除了在i++操作時使用synchronized關鍵字實現同步外,還可以使用AtomicInteger原子類進行實現。

    原子操作是不可分割的整體,沒有其他線程能夠中斷或檢查正在原子操作中的變量。它可以在沒有鎖的情況下做到線程安全。

    示例代碼:

    public class MyThread extends Thread { private static AtomicInteger count = new AtomicInteger(0); @Override public void run() { addCount(); } private static void addCount() { for (int i = 0;i<100;i++){ System.out.println(count.incrementAndGet()); } } public static void main(String[] args) { MyThread[] myThreads = new MyThread[100]; for (int i=0;i<100;i++){ myThreads[i] = new MyThread(); } for (int i=0;i<100;i++){ myThreads[i].start(); } }}

    打印結果:

    ....999699979998999910000

    成功達到累加的效果。

    5 原子類也不安全

    原子類在具有有邏輯性的情況下輸出結果也具有隨機性。

    示例代碼:

    public class MyThread extends Thread { private static AtomicInteger count = new AtomicInteger(0); public MyThread(String name) { super(name); } @Override public void run() { this.addCount(); } private void addCount() { System.out.println(Thread.currentThread().getName()+"加100之后:"+count.addAndGet(100)); count.addAndGet(1); } public static void main(String[] args) throws InterruptedException { MyThread[] myThreads = new MyThread[10]; for (int i = 0; i < 10; i++) { myThreads[i] = new MyThread("Thread-"+i); } for (int i = 0; i < 10; i++) { myThreads[i].start(); } Thread.sleep(2000); System.out.println(MyThread.count); }}

    打印結果:

    Thread-0加100之后:100Thread-2加100之后:201Thread-1加100之后:302Thread-5加100之后:602Thread-4加100之后:502Thread-3加100之后:402Thread-6加100之后:706Thread-7加100之后:807Thread-9加100之后:908Thread-8加100之后:10091010

    可以看到,結果值正確但是打印順序出錯了,出現這樣的原因是因為AtomicInteger的addAndGet()方法是原子的,但方法與方法之間的調用卻不是原子的。也就是方法addCount的調用不是原子的。解決這樣的問題必須要用同步。

    6 synchronized代碼塊有volatile同步的功能

    關鍵字synchronized可以使多個線程訪問同一個資源具有同步性,而且它還具有將線程工作內存中的私有變量與公共內存中的變量同步的功能。

    我們把前面講到的異步死循環代碼改造一下:

    public class PrintString implements Runnable{ private boolean isRunnning = true; @Override public void run() { String lock = new String(); System.out.println("Thread begin: "+Thread.currentThread().getName()); while (isRunnning == true){ synchronized (lock){ //加與不加的效果就是是否死循環 } } System.out.println("Thread end: "+Thread.currentThread().getName()); } public boolean isRunnning() { return isRunnning; } public void setRunnning(boolean runnning) { isRunnning = runnning; } public static void main(String[] args) throws InterruptedException { PrintString printString = new PrintString(); Thread thread = new Thread(printString,"Thread-A"); thread.start(); Thread.sleep(1000); printString.setRunnning(false); System.out.println("我要停止它!" + Thread.currentThread().getName()); }}

    打印結果:

    Thread begin: Thread-A我要停止它!mainThread end: Thread-A

    關鍵字synchronized可以保證在同一時刻,只有一個線程可以執行某一個方法或某一個代碼塊。它包含兩個特征:互斥相和可見性。同步synchronized不僅可以解決一個線程看到對象處于不一致的狀態,還可以保證進入同步方法或者同步代碼塊的每個線程,都看到由同一個鎖保護之前所有的修改效果。

    學習多線程并發。要著重“外修互斥,內修可見”,這是掌握多線程、學習多線程并發的重要技術點。

    私信我或關注公眾號猿來如此呀,回復:學習,獲取免費學習資源包

    來源網絡,侵權刪除

    總結

    以上是生活随笔為你收集整理的java多线程编程核心技术 pdf_Java多线程编程核心技术之volatile关键字的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。