Java多线程 volatile 作用与总结以及与synchronized关系

volatile的两点作用

  1. 保证可见性: 读取一个volatile 变量之前, 需要先使相应的本地缓存失效. 这样就必须到主内存读取最新的值,把读取到的最新的值, 放到子线程的工作内存中去. 写一个volatile 修饰的属性, 会立即刷入到主内存.
  2. 禁止指令重排: 解决单例双重锁乱序问题.
    关于指令重排序,之前写的如下这篇文章中有提到指令重排序问题.
    https://javaweixin6.blog.csdn.net/article/details/108417366
    只有发生了重排序, 才会出现x= 0 , y=0的情况.
    把上面文章中的代码, 修改如下, 变量加上volatile修饰, 那么就不会出现重排序问题了.

    也就是说不会出现上面文章中x和y都是打印0的情况了.
    运行如下, 没有出现打印出x和y为0的情况

volatile与synchronized 的关系

volatile可以看做是轻量级的synchronized .
如果一个共享变量, 一直只被各个线程赋值, 而没有其他的操作, 那么就可以用volatile来代替synchronized 或者代替原子变量, 因为对布尔变量的赋值自身是有原子性的, 而volatile保证了可见性, 就满足了线程安全.
因为布尔值, 只是改成true或者false, 具有原子性, 而不是像 a++操作是非原子性的.

volatile总结

  1. volatile的使用场景有限. 一个共享变量, 一直只被各个线程赋值, 而没有其他的操作, 那么就可以用volatile来代替synchronized 或者代替原子变量. 或者是用作触发器
  2. volatile 的属性和读写操作都是无锁的 ,不能替代synchronized , 因为volatile 没有提供原子性和互斥性. 但同时, 由于volatile 无锁, 不需要花费时间在获取锁和释放锁上, 因此是轻量级的.
  3. volatile 只能用作于属性上, 不像synchronized 是可以用在方法或者代码块上. 用volatile 修饰的属性, 编译器不会进行指令重排.
  4. volatile 提供了可见性, 任何一个线程对其的修改, 将立即对其他线程可见. volatile 修饰的属性不会被线程的工作内存缓存, 始终从主存中读取.
  5. volatile 提供了happens-before保证, 对volatile修饰的变量a一旦写入, 那么后续的操作一定是读取的最新的值.
  6. volatile 可以使得long 和double类型的赋值保证其原子性.

猜你喜欢

转载自blog.csdn.net/qq_33229669/article/details/108431303