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 |