写点什么

volatile 和 synchronzied 的区别

作者:悟空聊架构
  • 2021 年 12 月 16 日
  • 本文字数:967 字

    阅读完需:约 3 分钟

作者简介:悟空,8 年一线互联网开发和架构经验,用故事讲解分布式、架构设计、Java 核心技术。《JVM 性能优化实战》专栏作者,开源了《Spring Cloud 实战 PassJava》项目,公众号:悟空聊架构。本文已收录至 www.passjava.cn

volatile 和 synchronzied 的区别

  • volatile 只能修饰实例变量和类变量,synchronized 可以修饰方法和代码块。

  • volatile 不保证原子性,而 synchronized 保证原子性

  • volatile 不会造成阻塞,而 synchronized 可能会造成阻塞

  • volatile 轻量级锁,synchronized 重量级锁

  • volatile 和 synchronized 都保证了可见性和有序性

volatile 小结

  • volatile 保证了可见性:当一个线程修改了共享变量的值时,其他线程能够立即得知这个修改。

  • volatile 保证了单线程下指令不重排:通过插入内存屏障保证指令执行顺序。

  • volatitle 不保证原子性,如 a++这种自增操作是有并发风险的,比如扣减库存、发放优惠券的场景。

  • volatile 类型的 64 位的 long 型和 double 型变量,对该变量的读/写具有原子性。

  • volatile 可以用在双重检锁的单例模式种,比 synchronized 性能更好。

  • volatile 可以用在检查某个状态标记以判断是否退出循环。

补充案例

volatile 为什么不保证原子性,在单线程的场景,答案是 20000,如果是多线程的场景下呢?答案是可能是 20000,但很多情况下都是小于 20000。


怎么保证输出结果是 20000 呢?

synchronized 同步代码块

我们可以通过使用 synchronized 同步代码块来保证原子性。从而使结果等于 20000


public synchronized static void increase() {   number++;}
复制代码



但是使用 synchronized 太重了,会造成阻塞,只有一个线程能进入到这个方法。我们可以使用 Java 并发包(JUC)中的 AtomicInterger 工具包。

AtomicInterger 原子性操作

我们来看看 AtomicInterger 原子自增的方法 getAndIncrement()



public static AtomicInteger atomicInteger = new AtomicInteger();
public static void main(String[] args) {
for (int i = 0; i < 20; i++) { new Thread(() -> { for (int j = 0; j < 1000; j++) { atomicInteger.getAndIncrement(); } }, String.valueOf(i)).start(); }
// 当所有累加线程都结束 while(Thread.activeCount() > 2) { Thread.yield(); }
System.out.println(atomicInteger);}
复制代码


多次运行的结果都是 20000。



发布于: 3 小时前阅读数: 6
用户头像

用故事、大白话讲解Java、分布式、架构设计 2018.05.06 加入

公众号:「悟空聊架构」 【个人博客】www.passjava.cn 【开源项目】基于 SpringCloud 的一套面试刷题系统 【Github】https://github.com/Jackson0714/PassJava-Platform

评论

发布
暂无评论
volatile和synchronzied的区别