jvm-synchronized和volatile

synchronized

进入、退出同步块时会刷新所有工作内存,使得其他线程对刚退出同步块的线程对共享变量所作的更新可见。通过锁机制,synchronized实现了原子性和有序性,即同一时刻只能有一个线程进入临界区,且保证了下一个线程进入临界区之前上一个线程已经退出临界区。

volatile

同样通过刷新工作内存,实现了可见性,通过禁止指令重排序实现了有序性(个人理解:首先禁止编译器对jvm指令编译成机器指令时的重排序,其次禁止cpu执行机器指令时的指令重排序)。

  1 public class TestSynchronizedAndVolatile {
  2     private boolean b = false;
  3     private volatile Object lock = new Object();
  4 
  5     /**
  6      * 普通变量的写操作,不但会写入缓存,还会写回主内存
  7      */
  8     @Test
  9     public void test1() throws InterruptedException {
 10         new Thread(new Runnable() {
 11             @Override
 12             public void run() {
 13                 try {
 14                     /**
 15                      * 为了判断主线程中b=true是写入了主内存,还是仅写入工作内存。
 16                      * 之后的循环会跳出,说明了主线程中b=true回写到了主内存中。
 17                      */
 18                     Thread.sleep(100);
 19                 } catch (InterruptedException e) {
 20                 }
 21                 while (true) {
 22                     if (b) {
 23                         break;
 24                     }
 25                     b = false;
 26                 }
 27                 System.out.println("end");
 28             }
 29         }).start();
 30         b = true;
 31         Thread.sleep(1000);
 32     }
 33     /**
 34      * 线程中第一次读取到共享变量后,该变量会缓存在工作内存中,
 35      * 再次访问时直接从工作内存中读取,而不会重新从主内存中读取。
 36      * 这就造成了多线程访问共享变量时,访问到的是旧值。
 37      */
 38     @Test
 39     public void test2() throws InterruptedException {
 40         new Thread(new Runnable() {
 41             @Override
 42             public void run() {
 43                 while (true) {
 44                     /**
 45                      * 主线程中b=true后于子线程执行,虽然b=true已经作用于主内存,
 46                      * 但此前子线程已经将b读入工作内存(缓存),子线程没有动机再次从主线程同步b,
 47                      * 故这里会无限循环
 48                      */
 49                     if (b) {
 50                         break;
 51                     }
 52                 }
 53                 System.out.println("end");
 54             }
 55         }).start();
 56         Thread.sleep(100);
 57         b = true;
 58         Thread.sleep(100);
 59     }
 60 
 61     /**
 62      * synchronized会刷新缓存
 63      */
 64     @Test
 65     public void test3() throws InterruptedException {
 66         new Thread(new Runnable() {
 67             @Override
 68             public void run() {
 69                 Object lock = new Object();
 70                 while (true) {
 71                     /**
 72                      * 加入synchronized同步后情况发生了改变,当b=true同步回主内存后,
 73                      * 子线程中的b与主内存的值相同,循环跳出。
 74                      * 可见进入synchronized时会刷新工作内存(所有线程的工作内存),从而使得读取b时必须从主内存读取。
 75                      */
 76                     synchronized (lock) {
 77                         if (b) {
 78                             break;
 79                         }
 80                     }
 81                 }
 82                 System.out.println("end");
 83             }
 84         }).start();
 85         Thread.sleep(100);
 86         b = true;
 87         Thread.sleep(100);
 88     }
 89 
 90     /**
 91      * volatile会刷新缓存
 92      * @throws InterruptedException
 93      */
 94     @Test
 95     public void test4() throws InterruptedException {
 96         new Thread(new Runnable() {
 97             @Override
 98             public void run() {
 99                 Object obj = null;
100                 while (true) {
101                     /**
102                      * 加入synchronized同步后情况发生了改变,当b=true同步回主内存后,
103                      * 子线程中的b与主内存的值相同,循环跳出。
104                      * 可见进入synchronized时会刷新工作内存(所有线程的工作内存),从而使得读取b时必须从主内存读取。
105                      */
106                     if (b) {
107                         break;
108                     }
109                     System.out.println(b);
110                     /**
111                      * 由于lock是一个volatile,为了读到最新的lock值,这里就会刷新工作内存,
112                      * 这样的话,当再次进入循环时,b就需要从主内存读取,所以可以跳出循环。
113                      * (这里只体现了volatile可见性的语义,并没有体现禁止指令重排序的语义,
114                      * 个人对指令重排序的理解分为两层,一层是编译器将jvm指令转换为机器指令时的重排序,
115                      * 另一层是多核cpu并行执行指令造成的指令执行顺序的乱序。
116                      * 而可见性则可以通过刷新所有线程的工作内存实现。)
117                      */
118                     obj = lock;
119                     System.out.println("lock");
120                 }
121                 System.out.println("end");
122             }
123         }).start();
124         Thread.sleep(10);
125         b = true;
126         Thread.sleep(100);
127     }
128 }

 

posted @ 2017-09-06 16:31  holoyong  阅读(172)  评论(0编辑  收藏  举报