从synchronized的用法,到synchronized的底层实现原理,以及锁优化后,对各种优化策略的灵活使用。
01、synchronized作用
synchronized可以实现线程安全的3个方面:
1、原子性:确保线程互斥地访问同步代码。
2、可见性:保证共享变量的修改能够及时可见。其实是通过Java内存模型中的 “对一个变量unlock操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值” 来保证的;
3、有序性:有效解决重排序问题。即 “一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作”。
02、synchronized的使用方法
1、修饰实例方法,监视器锁是实例对象。
2、修饰静态方法,监视器锁是Class对象。
3、修饰同步代码块,监视器锁是synchronized括号里配置的对象。
03、synchronized的底层原理
1、synchronized修饰同步代码块时,是使用monitorenter和monitorexit指令实现的。
2、synchronized修饰方法时,是使用ACC_SYNCHRONIZED标识符来实现的。
04、对象头
synchronized用的锁是存在Java对象头里的。对象的内存布局如下
05、Lock Record
在线程进入同步代码块的时候,如果此同步对象没有被锁定(锁标记位为01),虚拟机将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word拷贝。
Lock Record是线程私有的,而Mark Word由于存放在对象头中,进而是存在Java堆中,所以Mark Word是线程共享的。
06、Monitor
Monitor可以理解为一种同步机制,或者一个对象。它存在于每一个Java对象的对象头Mark Word中。
当使用monitorenter指令获取对象的锁的时候,其实就是获取Monitor的所有权。
当使用monitorexit指令释放对象的锁的时候,其实就是释放Monitor的所有权。
07、锁优化
JDK 1.5引入了CAS操作,但是只是对J.U.C包进行了优化,并没有对synchronized关键字进行优化。在JDK 1.6之后,才开始对synchronized关键字进行优化。优化策略包括:CAS自旋、自适应的CAS自旋、锁消除、锁粗化、偏向锁、轻量级锁。
锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁、轻量级锁和重量级锁。随着竞争的加剧,锁的状态会逐渐升级。但是,锁只能升级不能降级。
1、自旋锁
当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否该锁被释放,而不是直接让线程挂起。
-XX:+UseSpinning 开启自旋锁
-XX:PreBlockSpin 设置自旋次数
2、自适应自旋锁
线程如果自旋成功了,那么下次自旋的次数会更加多,因为虚拟机认为既然上次成功了,那么此次自旋也很有可能会再次成功,那么它就会允许自旋等待持续的次数更多。反之,如果对于某个锁,很少有自旋能够成功,那么在以后要或者这个锁的时候自旋的次数会减少甚至省略掉自旋过程,以免浪费处理器资源。
3、锁消除
虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。
4、锁粗化
当有一系列的连续操作对同一个对象上反复加锁和解锁时,就把锁同步的范围扩展(粗化)到整个操作序列的外部。
5、偏向锁
偏向锁本质上是为了消除CAS,提高单线程执行同步代码块时的性能;轻量级锁是为了为了消除操作系统的互斥同步(Mutex
Lock),提高多线程并发执行同步代码块时的性能。
当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需要简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁。如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1:如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。如果竞争失败,偏向锁将升级为轻量级锁。
-XX:+UseBiasedLocking 设置偏向锁。默认开启偏向锁,如果关闭(“+”改成“-”),则直接进入轻量级锁。
6、轻量级锁
线程在访问同步块之前,虚拟机会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。
轻量级锁解锁时,会使用CAS操作将Mark Word拷贝替换回对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。
-XX:+UseHeavyMonitors 设置重量级锁。使用该参数,偏向锁和轻量级锁都会关闭。
08、VM参数设置
在eclipse中找到:Run——Run Configurations——VM
arguments,设置相应的参数即可。