Java并发编程 - volatile, synchronize关键字实现原理

本文探讨了Java并发编程中的关键字volatile和synchronized的实现原理。volatile确保共享变量的可见性,但不保证原子性,而synchronized则提供了更高级别的锁机制,包括偏向锁、轻量级锁和重量级锁。文章还讨论了CPU如何实现原子操作,如总线锁和缓存锁,并指出Java中原子操作主要通过CAS和锁机制来保证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java代码在编译后会变成Java字节码, 字节码被ClassLoader加载到JVM中, JVM执行字节码, 最终要转化为汇编指令在CPU上执行,
Java中所使用的并发机制依赖于JVM的实现和CPU的指令.

volatile原语

volatile是轻量级的synchronized, 它在多CPU(注意不仅仅是多核)开发中保证了共享变量的可见性.

由于volatile只保证了可见性, 无法保证原子性, 所以比synchronized的执行成本更低, 它不会引起上下文的切换和调度.

volatile实现原理

为加快CPU的处理速度, 它是不直接与内存进行通信, 而是先将系统内存的数据读到CPU内部缓存(L1, L2或其他)后再进行操作,
操作完全不知道何时会写回内存.

volatile标注的变量如果被修改了, 会引起一系列的变化:

  1. 当前操作的的CPU会强制将缓存行中的变化立即写回到内存中;
  2. 这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据立即失效, 也就是其他CPU如果需要再使用这个变量,
    需要重新到内存中读取到CPU缓存中;

synchronized原语实现原理与应用

Java中的每一个对象都可以作为锁

JavaSE 1.6中, 锁一共有四种状态, 级别从低到高依次为: 无锁状态, 偏向锁, 轻量级锁, 重量级锁. 当低级的锁机制不适配的时候, 程序会自动将锁升级为高级别的锁.

偏向锁

偏向锁使用了一种等到竞争出现才释放锁的机制, 所以当其他线程尝试竞争偏向锁时, 持有偏向锁的线程才会释放锁.

轻量级锁

在竞争锁的时候, 不会阻塞, 而是使用’自旋’机制反复尝试获取锁(始终消耗CPU时间片), 直到获取锁.

重量级锁

就是JavaSE 1.6之前的锁机制

锁的优缺点对比

类别优点缺点适用场景
偏向锁和执行非同步的方法相比仅存在纳秒级的差距如果线程间存在锁竞争, 则会带来额外的锁撤销的消耗适用于长期只有一个线程访问同步块的场景
轻量级锁竞争锁的线程不会阻塞, 提高了程序的响应速度竞争的线程会自旋, 消耗CPU追求响应时间, 且同步块执行速度非常快的场景
重量级锁竞争锁的线程不自旋, 不会消耗CPU竞争锁的线程会阻塞, 相应时间缓慢追求吞吐量, 或者是同步块执行时间较长的时候

原子操作的实现原理

原子操作的定义是:

不可被中断的一个或者一系列操作

CPU如何实现原子操作

总线锁

使用处理器提供的一个LOCK#信号, 当一个处理器在总线上输出此信号时, 其他处理器的请求将被阻塞, 那么该处理器可以独占共享内存.

缓存锁

同一时刻, 我们只需要保证对某个内存地址的操作是原子性的即可

Java如何实现原子操作

Java中可以通过锁和循环CAS(Compare and Set)的方式来实现原子操作, 具体的表现形式是内置了一些类来支持原子操作: 如AtomicBoolean,
AtomicInteger, AtomicLong

CAS实现原子操作的问题
  • 循环开销大:

    自旋CAS如果长时间不成功, 会给CPU带来非常大的执行开销

  • 只能保证一个共享变量的原子操作

    如果一个类有两个以上的field时, 就需要使用锁了.

使用锁机制来实现原子操作

实际上线程操作锁的方式在底层也是使用循环CAS的方式来进行锁的获取和释放的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值