600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Java并发编程学习笔记——volatile与synchronized关键字原理及使用

Java并发编程学习笔记——volatile与synchronized关键字原理及使用

时间:2021-01-04 18:16:24

相关推荐

Java并发编程学习笔记——volatile与synchronized关键字原理及使用

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

一、volatile关键字

1.简述

volatile是轻量级的synchronized,其在多处理器开发中保证了共享变量的“可见性”。

可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。

2.实现原理

Java代码最终都是需要转化为汇编指令在CPU上进行运行的。有volatile变量修饰的共享变量进行写操作的时候会多出第二行汇编代码,第二行汇编代码中包含有Lock前缀。

Lock前缀的指令在多核处理器下会引发了两件事:

将当前处理器缓存行的数据写回到系统内存。这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

原本处理器会将系统内存的数据读到内部缓存后进行操作,但操作完不知道何时会写到内存。所以当多线程并发的时候,其他处理器缓存的值还是旧的,再执行计算操作就会有问题。

而加上了volatile关键字,对变量进行写操作时,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。这样每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里。

3.两大原则

Lock前缀指令会引起处理器缓存回写到内存。Lock前缀指令导致在执行指令期间,声言处理器的LOCK#信号。在多处理器环境中,LOCK#信号确保在声言该信号期间,处理器可以独占任何共享内存一个处理器的缓存回写到内存会导致其他处理器的缓存无效。处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存的数据在总线上保持一致。

二、synchronized关键字

1.简述

当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。而synchronized实现同步的基础:Java中的每一个对象都可以作为锁,JVM基于进入和退出Monitor对象来实现方法同步和代码块同步。具体表现为以下3种形式:

对于普通同步方法,锁住当前实例对象。对于静态同步方法,锁是当前类的Class对象。对于同步方法块,锁是Synchonized括号里配置的对象。

2.锁与对象

synchronized用的锁是存在Java对象头里的。如果对象是数组类型,则虚拟机用3个字宽(Word)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。在32位虚拟机中,1字宽等于4字节,即32bit,在64位虚拟机中则为64bit。

在虚拟机中,对象头如下所示:

其中与锁相关的为Mark Word部分,其在32位虚拟机中的结构如下:

在运行期间,Mark Word里存储的数据会随着锁标志位的变化而变化。在32位虚拟机中Mark Word可能变化为存储以下4种数据:

在64位虚拟机下,Mark Word是64bit大小的:

3.锁的升级与对比

在Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,目的是为了提高获得锁和释放锁的效率。

3.1偏向锁

当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁。如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。

CAS:Compare and Swap,即比较再交换。其是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。是基于共享数据不会被修改的假设,适用于同步冲突出现的机会很少的情况。

3.2轻量级锁

线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

3.3锁的比较

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。