java多线程中 锁 的概念的理解_Java并发之锁的概念/同步关键字/synchronized在JVM的底层是如何实现的...

Java并发之锁的概念/同步关键字/synchronized在JVM的底层是如何实现的今天又是爱学习的一天哦~

日期:2019-6-27 21:10:45

锁的概念

自旋锁概念:不放弃CPU事件,不断使用 CAS 尝试对数据进行更新,直到成功

实例:AtomicInt 使用自旋锁,保证数据的原子性

其实也是一种乐观锁

乐观锁

悲观锁

独享锁(写锁)

共享锁(读锁)

可重入锁synchronized 就是可重入锁同一个线程,在拿到一次锁之后,可以继续调用同一把锁所同步的代码。

public synchronized void task(Object arg){

System.out.print(Thread.currentThread() + "开始执行" + arg);

if(arg == null){

this.task(new Object());

}

System.out.print(Thread.currentThread() + "执行结束" + arg);

}

//main

new Obj().task(null);

同步关键字

synchronized

线程封闭

锁消除对于某些局部变量的代码,可能不会出现线程安全问题,那样锁就会被消除

public void genStr(){

//JIT 优化,消除了锁

StringBuffer sb = new StringBuffer();

sb.append("a");

sb.append("b");

sb.append("c");

}

锁粗化属于运行时的 JIT 编译优化锁的范围会被扩大

sych 会锁到 for 循环外面

public class LockDemo01{

int count;

public void runTest(){

for(int i = 0; i < 10000; i++){

synchronized(this){

count++;

}

}

}

}

通过 JitWatch 查看经过 jit 优化后的代码http://www.cnblogs.com/stevenczp/p/7975776.htmlhttps://www.cnblogs.com/stevenczp/p/7978554.html

1、 输出jit日志 (windows)在jre/bin/server 放置hsdis动态链接库 eclise、idea等工具,加上JVM参数 -server -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+LogCompilation -XX:LogFile=jit.log

2、 工具安装 下载 https://github.com/AdoptOpenJDK/jitwatch 解压 通过maven运行 mvn clean compile exec:java

3、 配置jitwatch 页面选择 config, 配置要调试的项目src源码路径,和class编译路径 打开jit.log 点击start 4、 在分析的结果中,选中指定的类,再选择右侧的具体方法,则弹出jit编译结果

同步关键字加锁原理

大体结构

Obj Head 中的 mark wordBitFields 和 State 的对应关系。Lock Record Address 存储 thread stack 中 LockRecord 的地址** >>> 轻量级锁**

Monitor Address 存储 monitor obj 的地址 >>> 重量级锁因为涉及到了 monitor obj,所以可以认为是重量级的

TheadId 线程在 JVM 中的Id >>> 偏向锁

锁状态的改变

无锁 -> 轻量级锁CAS!CAS!CAS!CAS!CAS!CAS!CAS!

加锁

解锁

偏向锁 -> 轻量级锁问题:线程重入是如何判定的因为在 Lock Record Address 中记录了当前持有锁的线程的栈地址,所以可以比对出,是否可以重入

轻量级锁 -> 重量级锁

对象头中锁的状态(个人理解)

重量级锁/轻量级锁/未锁定相关代码:src.share.vm.runtime.ObjectMonitor当 线程-1 请求持有锁时,如果对象时 01 (unlock),则会获得锁,并将对象头中的标志位置为 轻量级锁 (00)

线程是通过 CAS 自旋的方式去请求锁的

当对象状态位 00 时,有其他线程请求锁,线程首先通过 CAS 自旋的方式去尝试获得锁,当尝试达到次数没有获得时,对象的状态会变为 10.

这里不再使用自旋等待的原因是自旋会消耗大量资源

之所以是重量级,是因为要操作两个对象 > 原对象、monitor 对象

此时对象的 monitor 中,_owner 会变为 线程-1,新的请求线程会放到 _EntryList 中等待

_EntryList 是争抢队列!!!

线程释放 _owner 会有两种方式

当持有锁的线程执行完后,会 monitorExit.

调用对象的 wait 方法后,会进入等待集合 _waiters

线程要进入 _waiters 队列,需要 _owner 线程调用 wait 方法,所以需要在同步代码块中执行 wait()/notify() 操作

因为等待集合是 Set,所以 notify 唤醒的时候不确定唤醒哪个

偏向锁优先级: 未锁定 >>> 偏向锁 >>> 轻量级 >>> 重量级轻量级锁的标志位存在 对象头 的 MarkWord 中,默认是开启的

具体过程线程1 请求持有锁,会先检查偏向锁锁的标志位,如果为 1 打开,则通过 CAS 修改线程信息。

线程信息中记录了当前获得偏向锁的线程的 ThreadId

如果修改成功,就拿到了锁

如果通过 CAS 修改失败,且线程状态是 01 未锁定,则升级到轻量级锁被清空标志位

偏向锁释放,会清空线程信息为什么要用偏向锁

因为 JVM 的设计理念认为,大多数情况下,并不存在锁竞争,不需要频繁修改标志位,减少了变为 重量级锁 的可能性。带来了性能提升。

参考

open jdk

版权声明本文作者: 留夕

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 许可协议。转载请注明出处!

首发日期: 2019-6-27 21:10:45

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值