Synchronized 和volatile的异同

1. 简介

我常常使用synchronized和volatile在我们的程序中保证在并发中数据的安全,但是我们怎么理解他们了??

2.JMM(Java Memory Model)

JMM 是一个JVM对主内存和缓存和CPU之间的关系做了简化抽象,在实际情况下并不存在.在下图 ThreadStack 对应缓存和CPU的寄存器,而Heap对应主内存


image.png

3. 异同

  1. Synchronized 是修饰代码块和方法的,而volatile是修饰变量的
  2. Synchronized 是保证原子性和有序性和可见性,而volatile只有 有序性和可见性
    3 Synchronized 会导致线程上下文切换,而 volatile不会,这就会使volatile的效率高于synchronized
  3. synchronized是在读入变量的时候,会清空当前线程的缓存,直接从主内存中读取,而volatile是在其中有一个线程进行写操作结束的时候,会通知其他线程他们缓存失效,需要从主内存中读取,用volatile修饰的变量所有的写操作都优先于读操作。(任何变量进行写操作的时候都会写入主内存,而读取就优先从缓存中读取)

4 一个简单的例子

这个例子我们会在多线程下,执行对同一个变量进行修改和读取

4.1 我对写操作使用synchronized 和读操作不使用,变量也不用volatile修饰

package liusheng.main.liusheng.main;

import liusheng.main.ConcurrentQueue;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class SynAndVolatile {
    static class TestClass {
        int num = 0;

        public synchronized void add() {
            num++;
        }

        public int getNum() {
            return num;
        }
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        TestClass testClass = new TestClass();
        IntStream.range(0, 10).boxed().map(i -> (Runnable) () -> {
                    for (int j = 0; j < 10000; j++) {
                        testClass.add();
                    }
                    System.out.println("完成");
                }
        ).forEach(executorService::execute);
        for (; ; ) {
            if (testClass.getNum() == 100000) {
                System.out.println("SUCCESS");
            }
        }
    }


}

4.1 结果

image.png

我们发现程序卡主了,读取不出num的值,也就是main线程是在缓存中读取的,不是在主内存中读取的,所以发现不了num值的变化

4.2 我们对num进行volatile修饰,其他的还是一样的

  volatile int num = 0;

4.2 结果


我们发现主内存检查到了num值发生的变化,当num值发生变化的时候会通知其他(含有num副本的现场)线程,这num副本失效,需要从主内存从新读取。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容