volatile概念: volatile 關鍵字的主要做用是使變量在多個線程中可見。java
在 java 中 , 每個線程都會有一塊工做內存區 , 其中存放着全部線程共享的主內存中的變量值的拷貝。當線程執行時 , 他在本身的工做內存區中操做這些變量。爲了存取一個共享的變量 , 一個線程一般先獲取鎖定並去清楚它的內存工做區 , 把這些共享變量從全部線程的共享內存區中正確的裝入到它本身所在的工做內存區中 , 當線程解鎖時保證該工做內存中變量的值寫回到共享內存中。安全
volatile 的做用就是強制線程到主內存 (共享內存) 裏去讀取變量 , 而不去線程工做內存區裏去讀取 , 從而實現了多線程間的變量可見。也就是知足線程安全的可見性。多線程
1 package com.itdoc.multi.sync007; 2 3 /** 4 * @BLOG http://www.cnblogs.com/goodcheap 5 * @DESCRIBE volatile 關鍵字的主要做用是使變量在多線程中可見。 6 * @AUTHOR WángChéngDá 7 * @DATE 2017-03-22 17:43 8 */ 9 public class RunThread extends Thread { 10 11 private boolean isRunning = true; 12 13 public void setRunning(boolean running) { 14 isRunning = running; 15 } 16 17 @Override 18 public void run() { 19 System.out.println("Enter the run method..."); 20 int i = 0; 21 while (isRunning) { 22 i++; 23 // System.out.println(i); 如果有 print 或 println 語句會跳出死循環。 24 } 25 System.out.println("Thread stop..."); 26 } 27 28 public static void main(String[] args) throws InterruptedException { 29 RunThread rt = new RunThread(); 30 rt.start(); 31 Thread.sleep(3000); 32 rt.setRunning(false); 33 System.out.println("The value of isRunning has been set to false..."); 34 35 } 36 }
控制檯輸出:ide
Enter the run method... |
程序沒有結束 , 一直在死循環中(CPU:4 代 I3 或 3 代 I5 以上處理器不會出現死循環)。this
分析圖解:atom
1 package com.itdoc.multi.sync007; 2 3 /** 4 * @BLOG http://www.cnblogs.com/goodcheap 5 * @DESCRIBE volatile 關鍵字的主要做用是使變量在多線程中可見。 6 * @AUTHOR WángChéngDá 7 * @DATE 2017-03-22 17:43 8 */ 9 public class RunThread extends Thread { 10 11 private boolean isRunning = true; 12 13 public void setRunning(boolean running) { 14 isRunning = running; 15 } 16 17 @Override 18 public void run() { 19 System.out.println("Enter the run method..."); 20 int i = 0; 21 while (isRunning) { 22 i++; 23 // System.out.println(i); 24 } 25 System.out.println("Thread stop..."); 26 } 27 28 public static void main(String[] args) throws InterruptedException { 29 RunThread rt = new RunThread(); 30 rt.start(); 31 Thread.sleep(3000); 32 rt.setRunning(false); 33 System.out.println("The value of isRunning has been set to false..."); 34 35 } 36 }
控制檯輸出:spa
Enter the run method... |
volatile 關鍵字不具有 synchronized 關鍵字的原子性 (同步)。code
1 package com.itdoc.multi.sync007; 2 3 /** 4 * @BLOG http://www.cnblogs.com/goodcheap 5 * @DESCRIBE volatile 關鍵字不具有 synchronized 關鍵字的原子性 (同步) 6 * @AUTHOR WángChéngDá 7 * @DATE 2017-03-24 10:40 8 */ 9 public class VolatileNoAtomic extends Thread { 10 11 private volatile static int count = 0; 12 13 public void addCount() { 14 /** 15 * 若 volatile 具備原子性, 打印最後一個結果必定是 10000, 16 * 如有不是 10000 的時候, 說明 volatile 不具有原子性。 17 */ 18 for (int i = 0; i < 1000; i++) { 19 count++; 20 } 21 System.out.println(count); 22 } 23 24 @Override 25 public void run() { 26 this.addCount(); 27 } 28 29 public static void main(String[] args) { 30 VolatileNoAtomic[] arr = new VolatileNoAtomic[100]; 31 for (int i = 0; i < 10; i++) { 32 arr[i] = new VolatileNoAtomic(); 33 arr[i].start(); 34 } 35 } 36 }
控制檯輸出:
1000 |
以上數聽說明 , volatile 關鍵字不具有原子性。
volatile 關鍵字只有可見性 , 沒有原子性。要實現原子性建議使用 atomic 類的系列對象 , 支持原子性操做 (注意 : atomic 類只保證自己方法的原子性 , 並不能保證屢次操做的原子性)。
1 package com.itdoc.multi.sync007; 2 3 import java.util.concurrent.atomic.AtomicInteger; 4 5 /** 6 * @BLOG http://www.cnblogs.com/goodcheap 7 * @DESCRIBE volatile 關鍵字不具有 synchronized 關鍵字的原子性 (同步) 8 * @AUTHOR WángChéngDá 9 * @DATE 2017-03-24 10:40 10 */ 11 public class VolatileNoAtomic extends Thread { 12 13 private static AtomicInteger count = new AtomicInteger(0); 14 public void addCount() { 15 /** 16 * 若 AtomicInteger 具備原子性, 打印最後一個結果必定是 10000, 17 * 如有不是 10000 的時候, 說明 AtomicInteger 不具有原子性。 18 */ 19 for (int i = 0; i < 1000; i++) { 20 count.incrementAndGet(); 21 } 22 System.out.println(count); 23 } 24 25 @Override 26 public void run() { 27 this.addCount(); 28 } 29 30 public static void main(String[] args) { 31 VolatileNoAtomic[] arr = new VolatileNoAtomic[100]; 32 for (int i = 0; i < 10; i++) { 33 arr[i] = new VolatileNoAtomic(); 34 arr[i].start(); 35 } 36 } 37 }
控制檯輸出:
1000 |
AtomicInteger 是具備原子性的 , 但只針對結果 , 得須要 synchronized 配合才能夠實現線程過程當中的原子性。理想狀態是沒一個線程打印輸出都是添加1000。
1 package com.itdoc.multi.sync007; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import java.util.concurrent.atomic.AtomicInteger; 6 7 /** 8 * @BLOG http://www.cnblogs.com/goodcheap 9 * @DESCRIBE AtomicInteger 原子性測試 10 * @AUTHOR WángChéngDá 11 * @DATE 2017-03-24 11:00 12 */ 13 public class AtomicUse { 14 15 private static AtomicInteger count = new AtomicInteger(0); 16 17 /** 18 * 多個 addAndGet 在一個方法內是非原子性的, 須要加 synchronized 修飾, 保證整個總體的原子性。 19 */ 20 public int multiAdd() { 21 try { 22 Thread.sleep(1000); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 /** 27 * 若每次運算結果都是加 10 證實這個總體是原子性的。 28 */ 29 count.addAndGet(1); 30 count.addAndGet(2); 31 count.addAndGet(3); 32 count.addAndGet(4);// +10 33 return count.get(); 34 } 35 36 public static void main(String[] args) { 37 final AtomicUse au = new AtomicUse(); 38 List<Thread> lt = new ArrayList<Thread>(); 39 for (int i = 0; i < 10; i++) { 40 lt.add(new Thread(() -> System.out.println(au.multiAdd()))); 41 } 42 lt.forEach((i) -> i.start()); 43 // lt.forEach(Thread::start); 44 } 45 }
控制檯輸出:
30 |
每次運算都是 +10 , 整個總體纔是原子性的。
1 package com.itdoc.multi.sync007; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import java.util.concurrent.atomic.AtomicInteger; 6 7 /** 8 * @BLOG http://www.cnblogs.com/goodcheap 9 * @DESCRIBE AtomicInteger 原子性測試 10 * @AUTHOR WángChéngDá 11 * @DATE 2017-03-24 11:00 12 */ 13 public class AtomicUse { 14 15 private static AtomicInteger count = new AtomicInteger(0); 16 17 /** 18 * 多個 addAndGet 在一個方法內是非原子性的, 須要加 synchronized 修飾, 保證整個總體的原子性。 19 */ 20 public synchronized int multiAdd() { 21 try { 22 Thread.sleep(1000); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 /** 27 * 若每次運算結果都是加 10 證實這個總體是原子性的。 28 */ 29 count.addAndGet(1); 30 count.addAndGet(2); 31 count.addAndGet(3); 32 count.addAndGet(4);// +10 33 return count.get(); 34 } 35 36 public static void main(String[] args) { 37 final AtomicUse au = new AtomicUse(); 38 List<Thread> lt = new ArrayList<Thread>(); 39 for (int i = 0; i < 10; i++) { 40 lt.add(new Thread(() -> System.out.println(au.multiAdd()))); 41 } 42 lt.forEach((i) -> i.start()); 43 // lt.forEach(Thread::start); 44 } 45 }
控制檯輸出:
102030405060708090100 |