JVM线程安全与锁优化

Java实现同步有3个基础手段,synchronized,AQS+CAS配合实现的对象锁,cas

 

 

synchronized修饰的代码块中,在java文件被编译成class文件后,生成了字节码,在代码块之前,会多出来一条monitor enter指令。在代码块之后,会多出来一条monitor exit指令。

同时reference需要被指定,因为这是对象锁

 

这里总结下synchronized和ReentranLock默认实现的区别

都是悲观等待的

都是可重入

都是非公平

都是可以有条件休眠的,但是后者可以实现多条件

后者可以响应中断,先修改中断标记位,在等待的时候被唤醒会检测

 

乐观锁

正常修改一个值的操作

1.从主内存读取值到私有内存(寄存器)

2.修改这个值

3.可能再次复用这个值,进行修改

4.把这个值从私有内存刷新回主内存

加了volatile

1.从主内存读取值到私有内存

2.修改这个值

3.把这个值从私有内存刷新回主内存

4.再次读取主内存的值

所以volatile是不顶用的。

cas:

1.从主内存读取值到私有内存

2.修改这个值

3.尝试把这个值从私有内存刷新回主内存,如果旧值和现值不一致,失败

这3个操作被打包成一条指令

 

volatile

1.强制从主内存读取(结合java内存模型)

2.不保证原子性

3.指令重排序引起的内存屏障。比如

config();

boolean init = true;

这个init被赋值为true可能会先被执行。

 

可重入代码

比如不依赖外部变量,只依赖方法内的局部变量表里的变量。同时多个线程执行他会得到相同的结果。

 

线程本地存储

ThreadLocal。比如Looper在初始化的时候会被设置到ThreadLocal里面,其原理是以当前线程为键,获取一个map,再根据ThreadLocal为键,获取ThreadLocal的值。使得一个成员变量,在不同的线程会有不同的实例。

 

自旋

由于线程从用户态到内核态的切换需要时间,可以在发现资源被占用的时候,可以在内核态自旋几次,如果还没有,那就再睡眠。

如果之前成功通过自旋的方式等到锁了,那么下次自旋的时候,自旋次数会被增加。

 

锁清除

如果没必要的锁,JVM会替我们清除

 

锁合并

许多锁放在一起不太好,可以考虑合并

 

Mark Word

对象头。存储了gc年龄、hashcode、锁标志。

 

偏心锁、轻量级锁、重量级锁

偏心锁认为只有一个线程会来访问这个对象。一开始对象是可偏向状态。当第一次加锁的时候,会把ThreadID记录在Mark Work中。

接下来如果有别的线程来访问,就会去检查ThreadID的那个线程死了没有、或者这个对象是否还会被那个线程用到。如果没有,那么这个新的线程就被偏心了。如果有,那么就会膨胀程轻量级锁。

轻量级锁认为竞争是有的,但是很轻。倾向于通过自旋来解决问题。如果自旋次数太多,或者在自旋等待的时候,又有新的线程到来,那么就膨胀为重量级锁(不会再自旋,直接休眠之)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值