Java并发机制的底层实现原理

本文包含内容

  • volatile 实现原理
  • synchronized 实现原理及应用
  • synchronized 锁升级过程
  • java 如何实现原子操作
  • 锁与CAS
  • CAS常见问题与解决

volatile 实现原理

定义

java语言规范第三版中对volatile定义如下:

Java编程语言允许线程访问共享变量,为了确保共享变量能准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。

如果一个字段被声明成volatile,java线程内存模型确保所有线程看到这个变量的值是一致的。

实现原理

大家都知道,为了提高运行速度,处理器不会直接与内存通信,而是通过内部高速缓存(L1、L2、或其他)间接与内存通信,在高速缓存中操作后的缓存数据写回到主存的时间是不一定的。在JVM执行字节码过程中,对于声明了volatile关键字的变量,当线程执行了写操作后,JVM会向处理器发送一条Lock前缀的指令,将这个变量所在的缓存行数据写回到系统内存。

但是,光写回到主存是不够的,因为其他处理器的缓存值仍然没有更新。在多处理器下,为了保证各处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己的缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置为无效状态,这就是缓存失效 当处理器对这个数据进行修改操作的时候,会重新到主存中进行加载,这样就实现了 内存可见

其他特性

volatile除了保证修饰变量的内存可见性之外,还有一个特性是可以防止JVM的指令重排优化,这里暂时不打算展开,有时间补上。

synchronized 实现原理及应用

除了volatile,Java并发编程中使用较多的还有synchronized关键字。

jdk1.6之前,人们称它为“重量级锁”,因为它的实现方式是操作系统级别的,锁从内核态到用户态的相互转换比较耗费性能。

接下来详解jdk1.6之后,为了减少获得锁与释放锁带来的性能消耗而引入的偏向锁和轻量级锁,以及锁的存储结构和升级过程。

利用synchronized实现同步的基础:java中每一个对象都可以作为锁,具体表现为以下三种形式:

  • 对于普通同步方法,锁的是当前实例对象
  • 对于静态同步方法,锁的是当前类的类对象(Class对象)
  • 对于同步代码块,锁住的是synchronized括号中所指定的对象
实现原理

从JVM规范中可以看到:JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,代码块同步时使用monitorentermonitorexit指令实现。

monitorener指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到同步代码块结束处或者异常处。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

synchronized 锁升级过程

在jdk1.6中 synchronized锁一共有四种状态,分别为“无锁状态”、“偏向锁状态”、“轻量级锁”、“重量级锁状态"。几种状态只能升级不能降级。

偏向锁:大多数情况下,锁不仅不存在多线程竞争,甚至总是由同一个线程持有,为了适应这种情况引入了偏向锁,当线程访问同步块获得锁时,会在对象头和栈帧处记录锁偏向的线程ID,之后这个线程进入同步块时不再需要CAS操作来加锁和解锁,只需简单测试对象头mark word中是否存储着指向该线程的偏向锁。

如果测试成功,则线程重入,如果测试失败,先验证对象头偏向锁标识是否为1,如果不是1,则CAS竞争锁,如果是1,则尝试使用CAS将对象头的偏向锁指向当前线程。

轻量级锁:当竞争出现时,持有偏向锁的线程会释放锁,释放锁时,会等到全局安全点(这个时间点上没有执行的字节码)。释放结束后锁会升级为轻量级锁,此时线程会尝试使用CAS持有锁,如果失败,将会使用自旋的方式重试获得锁。

若当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定的次数时,轻量级锁便会升级为重量级锁(锁膨胀)。

另外,当一个线程已持有锁,另一个线程在自旋,而此时又有第三个线程来访时,轻量级锁也会升级为重量级锁(锁膨胀)。

java 如何实现原子操作

原子本意是”不能被进一步分割的最小粒子“,java中的原子操作是指”对变量的一系列操作,要么全部成功,要么全部失败“,并发编程中,只有保证一些操作的原子性,才不至于产生错误的结果

处理器如何处理
  1. 通过总线锁保证操作原子性
    使用处理器提供的一个Lock#信号,当一个处理器在总线上输出此信号时,其他处理器请求将被阻塞住,那么该处理器独享内存,使用总线锁的开销是很大的,因为在锁定期间,其他处理器不能操作其他内存地址的数据。
  2. 使用缓存锁保证操作原子性
    很好理解,当内存内某处理器使用缓存锁锁住内存某区域地址,那么处理器将不能同时缓存该缓存行
JAVA中的实现机制

Java中通过 CAS 来实现原子操作。

通过锁可以实现同一时间只有一个线程操作数据,自然保证了操作安全,这里重点说一下CAS 。

自旋操作通过与预期值比较来判断是否可以进行结果写入,当预期不符时,将重新执行并再次判断,直到写入成功。

CAS 实现原子操作的三大问题
  • ABA问题
    如果一个值原来是A,变成了B,然后又变成了A,在CAS看来这个变量没有发生变化,而实际上却变化了,解决思路是通过版本号来判断是否发生了改变。从jdk1.5开始,Atomic包中提供了AtomicStampedReference来解决ABA问题。
  • 循环时间长,开销大
    很显然,自旋时间长,会给cpu带来非常大的执行开销,解决此问题的途径是JVM支持处理器提供的pause指令,这里暂时不作展开。
  • 只能保证一个共享变量的原子操作
    CAS只能保证单个变量操作的原子性,从jdk1.5开始,jdk提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作。

小结

通过本文浅显的介绍,可以了解volatile、synchronized和原子操作的实现原理。java中大部分容器和框架都依赖此部分基础知识,相信会对大家今后的并发编程有一定帮助。

线程的状态以及各状态之间的转换详解.mp4 线程的初始化,中断以及其源码讲解.mp4 多种创建线程的方式案例演示(一)带返回值的方式.mp4 多种创建线程的方式案例演示(二)使用线程池.mp4 Spring对并发的支持:Spring的异步任务.mp4 使用jdk8提供的lambda进行并行计算.mp4 了解多线程所带来的安全风险.mp4 从线程的优先级看饥饿问题.mp4 从Java字节码的角度看线程安全性问题.mp4 synchronized保证线程安全的原理(理论层面).mp4 synchronized保证线程安全的原理(jvm层面).mp4 单例问题与线程安全性深入解析.mp4 理解自旋锁,死锁与重入锁.mp4 深入理解volatile原理与使用.mp4 JDK5提供的原子类的操作以及实现原理.mp4 Lock接口认识与使用.mp4 手动实现一个可重入锁.mp4 AbstractQueuedSynchronizer(AQS)详解.mp4 使用AQS重写自己的锁.mp4 重入锁原理与演示.mp4 读写锁认识与原理.mp4 细读ReentrantReadWriteLock源码.mp4 ReentrantReadWriteLock锁降级详解.mp4 线程安全性问题简单总结.mp4 线程之间的通信之wait notify.mp4 通过生产者消费者模型理解等待唤醒机制.mp4 Condition的使用及原理解析.mp4 使用Condition重写waitnotify案例并实现一个有界队列.mp4 深入解析Condition源码.mp4 实战:简易数据连接池.mp4 线程之间通信之join应用与实现原理剖析.mp4 ThreadLocal 使用及实现原理.mp4 并发工具类CountDownLatch详解.mp4 并发工具类CyclicBarrier 详解.mp4 并发工具类Semaphore详解.mp4 并发工具类Exchanger详解.mp4 CountDownLatch,CyclicBarrier,Semaphore源码解析.mp4 提前完成任务之FutureTask使用.mp4 Future设计模式实现(实现类似于JDK提供的Future).mp4 Future源码解读.mp4 ForkJoin框架详解.mp4 同步容器与并发容器.mp4 并发容器CopyOnWriteArrayList原理与使用.mp4 并发容器ConcurrentLinkedQueue原理与使用.mp4 Java中的阻塞队列原理与使用.mp4 实战:简单实现消息队列.mp4 并发容器ConcurrentHashMap原理与使用.mp4 线程池的原理与使用.mp4 Executor框架详解.mp4 实战:简易web服务器(一).mp4 实战:简易web服务器(二).mp4 JDK8的新增原子操作类LongAddr原理与使用.mp4 JDK8新增锁StampedLock详解.mp4 重排序问题.mp4 happens-before简单概述.mp4 锁的内存语义.mp4 volatile内存语义.mp4 final域的内存语义.mp4 实战:问题定位.mp4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hongmin.shm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值