volatile 关键字

volatile概念: volatile 关键字的主要作用是使变量在多个线程中可见。

       在 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 }

控制台输出:

Enter the run method...
The value of isRunning has been set to false...

程序没有结束 , 一直在死循环中(CPU:4 代 I3 或 3 代 I5 以上处理器不会出现死循环)。

 分析图解:

 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 }

控制台输出:

Enter the run method...
The value of isRunning has been set to false...
Thread stop...

volatile 关键字不具备 synchronized 关键字的原子性 (同步)。

 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
2000
3000
4026
4299
5299
6299
7299
8299
9299

以上数据说明 , 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
2000
3000
4474
5000
6058
7000
8000
9129
10000

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
40
30
30
60
100
80
90
70
61

每次运算都是 +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 }

控制台输出:

10
20
30
40
50
60
70
80
90
100

原文地址:https://www.cnblogs.com/chinda/p/6601399.html