征婚网站建设,网站自助平台,谷歌镜像网站怎么做,wordpress账号密码数据库目录
一、基本特点
二、加锁过程
2.1、偏向锁
2.2、轻量级锁
2.3、重量级锁
三、其它的优化操作
3.1、锁消除
3.2、锁粗化 一、基本特点
synchronized有以下特性:
开始是乐观锁#xff0c;如果锁冲突频繁#xff0c;就转换为悲观锁。开始是轻量级锁#xff0c…目录
一、基本特点
二、加锁过程
2.1、偏向锁
2.2、轻量级锁
2.3、重量级锁
三、其它的优化操作
3.1、锁消除
3.2、锁粗化 一、基本特点
synchronized有以下特性:
开始是乐观锁如果锁冲突频繁就转换为悲观锁。开始是轻量级锁如果锁被持有的时间较长就转换为重量级锁。实现轻量级锁的时候大概率用到的自旋锁策略。是一种不公平锁。是一种可重入锁。不是读写锁。
二、加锁过程
JVM会将synchronizde锁分为无锁偏向锁轻量级锁重量级锁状态。根据锁的激烈程度自动进行锁升级。 举个栗子 1、我们在学校每天早上去图书馆学习‘坤坤’已经在图书馆学习时长两年半所以坤坤每天都要起一个大早去学习他真的很努力所以他到了图书馆基本上没有人坤坤可以随便坐。 2、过了一个小时有真爱粉来图书馆找咯咯一起学习真爱粉就把书本放在桌子上标志着这个地方有人了给自己占了个位置其他人不准用。 3、 随着真爱粉越来越多学习的人也越来越多图书馆也就慢慢有了竞争于是有的同学的书本就会被放在一边自己的位置被别人占了真爱粉回来之后发现座位被占了周围也满了她就站在原地一直等相当于自旋状态可以在第一时间发现空余作为效率比较高。 4、听说咯咯每天都去图书馆学习小黑子门也急了他们也要去图书占位于是图书馆的座位竞争越来越激烈高峰时期就要排队(阻塞队列)阻塞队列也就越来越长每个人都要检查锁如果没有可用的锁就要去排队对于synchronized来说相当于是重量级锁会调用内核态的加锁指令来完成获取锁操作。 2.1、偏向锁
第一个尝试加锁的线程会优先进入偏向锁。 偏向锁不是真的”加锁“而是给对象头加了一个”偏向锁标记“,记录这个锁属于哪个线程。 如果后续没有其它线程来竞争该锁那么就不用进行其它同步操作了(避免了加锁解锁的资源开销)如果后续有其它线程来竞争该锁那就取消原来的偏向锁状态进入一般的轻量级锁状态。 偏向锁的本质上相当于”延迟加锁“能不加锁就不加锁避免资源的浪费。 但是该做标记还是要做否则无法区分何时真正加锁。 2.2、轻量级锁
随着其它线程进入竞争偏向锁状态被解除进入轻量级锁状态
此处的轻量级锁就通过CAS(自旋)来实现. 通过CAS检查并更新一块内存(比如null该线程引用)。如果更新成功则认为加锁成功。如果更新失败则认为锁被占用继续自旋式等待锁释放(并不放弃CPU)。 自旋操作是让CPU一直空转比较浪费资源因此自旋状态不会一直持续进行而是达到一定时间(重试次数),就不再自旋也就是”自适应“。
2.3、重量级锁
如果锁竞争进一步激烈自旋不能快速获取到锁就会膨胀为重量级锁。
此处的重量级锁就是指内核提供的mutex. 执行加锁操作先进入内核状态。在内核判定当前锁是否已经被占用。如果该锁没有被占用则加锁成功并切换回用户态。如果该锁被占用则加锁失败。此时线程进入锁的等待队列挂起等待被唤醒。经历了一系列操作这个锁被其它线程释放了操作系统也想起了这个被挂起的线程于是唤醒这个线程尝试重新获取锁。 打印类的布局 import org.openjdk.jol.info.ClassLayout;/*** 打印类的布局*/
public class Exe_01 {//定义变量private int count 100;private long count1 200;private String hello ;private TextExe_01 textExe_01 new TextExe_01();public static void main(String[] args) throws InterruptedException {//创建一个对象的实例Object obj new Object();//打印实例布局System.out.println(任意object对象布局起初无锁状态);System.out.println(ClassLayout.parseInstance(obj).toPrintable());System.out.println(延迟三秒开启偏向锁);//延迟3秒开启偏向锁Thread.sleep(3000);//创建本类的实例Exe_01 monitor new Exe_01();//打印实例布局查看锁状态System.out.println(打印实例布局注意查看锁状态为偏向锁);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());//调用hashCode后保存hashCode的值monitor.hashCode();//观察现象System.out.println(ClassLayout.parseInstance(monitor).toPrintable());System.out.println();System.out.println(synchronized加锁);//加锁后观察锁信息synchronized (monitor) {System.out.println(第一层synchronized加锁后);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());//锁重入观察锁信息synchronized (monitor) {System.out.println(第二层synchronized加锁后,锁重入);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());}//释放里层的锁System.out.println(释放内层锁后);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());}//释放所有锁之后System.out.println(释放所有锁);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());System.out.println( 多个线程参与锁竞争观察锁状态);//强制执行垃圾回收System.gc();//观察GC计数System.out.println(调用GC后查看age的值);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());//打印类布局调用不同的方法查看System.out.println(查看类布局);System.out.println(ClassLayout.parseClass(Exe_01.class).toPrintable());//打印类对象布局System.out.println(查看类对象布局);System.out.println(ClassLayout.parseInstance(Exe_01.class).toPrintable());synchronized (Exe_01.class) {//加锁后的类对象System.out.println(对类对象加锁后不同的对象获取锁观察锁升级为thin lock);System.out.println(ClassLayout.parseInstance(Exe_01.class).toPrintable());}//释放锁之后的类对象System.out.println(释放锁后);System.out.println(ClassLayout.parseInstance(Exe_01.class).toPrintable());System.out.println(多个锁线程参与锁竞争观察锁状态);Thread thread1 new Thread(() - {synchronized (monitor) {System.out.println( 在线程A 中获取锁参与锁竞争当前只有线程A 竞争锁轻度锁竞争);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());}});thread1.start();// 休眠一会不与线程A 激烈竞争Thread.sleep(100);Thread thread2 new Thread(() - {synchronized (monitor) {System.out.println( 在线程B 中获取锁与其他线程进行锁竞争);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());}});thread2.start();// 不休眠直接竞争锁产生激烈竞争System.out.println( 不休眠直接竞争锁产生激烈竞争);synchronized (monitor) {// 加锁后的类对象System.out.println( 与线程B 产生激烈的锁竞争观察锁状态为fat lock);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());}// 休眠一会释放锁后Thread.sleep(100);System.out.println( 释放锁后);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());System.out.println();// 调用hashCode后才保存hashCode的值monitor.hashCode();// 调用hashCode后观察现象System.out.println( 调用hashCode后查看hashCode的值);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());// 强制执行垃圾回收System.gc();// 观察GC计数System.out.println( 调用GC后查看age的值);System.out.println(ClassLayout.parseInstance(monitor).toPrintable());// 打印类布局注意调用的方法不同System.out.println( 查看类布局);System.out.println(ClassLayout.parseClass(Exe_01.class).toPrintable());// 打印类对象布局System.out.println( 查看类对象布局);System.out.println(ClassLayout.parseInstance(Exe_01.class).toPrintable());}
}class TextExe_01 {}结果分析
里面的参数类型可参考这篇文章-Java对象的内存布局 - JaJian - 博客园 (cnblogs.com) 三、其它的优化操作
3.1、锁消除
synchronized的一种优化策略
synchronized是手动添加的自己获取到锁的处理逻辑什么时候加加在哪里JVM无法去管理但是在代码编译和运行的时候JVM可以知道程序是读变量还是写变量。
如果我们手动对所有的读操作都加了synchronized关键字但是又没有写操作那么这个时候JVM就认为这个锁是多余的那么synchronized就不会去枷锁了这种现象就叫做”锁消除“。
注意
多线程状态下多线程对同一个变量进行修改才会有线程安全问题而多线程对同一个变量读取没有线程安全问题。
synchronized只有100%确定不需要锁的时候才会进行锁消除优化 。
3.2、锁粗化
一段逻辑中如果出现多次加锁解锁编译器JVM就会自动进行锁粗话。 真实的代码执行过程从方法1到方法5全部都执行完才结束。 如上面的流程中每一个方法都会有一个申请锁资源释放锁资源的流程。 那么对于这种现象synchronized就会认为从头到尾只需要获取一次锁资源中途不会释放锁。 这个现象就叫“锁粗化”从方法级别(细粒度变成了业务级别的粗粒度)。