• 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 }
  • 相关阅读:
    HDU4411 最小费用流
    HDU5934 强连通分量
    一个问题
    SAP模板
    KMP模板
    ]C#中执行SQL文件脚本的代码(非常有用)
    C#调用非托管程序5种方式
    [转]C#中的静态常量(const)和动态常量(static和readonly)用法和区别
    [转]C#开发命名规范总结整理
    [转]关于同步方法里面调用异步方法的探究
  • 原文地址:https://www.cnblogs.com/holoyong/p/7485546.html
Copyright © 2020-2023  润新知