并发的挑战
上下文切换: 是消耗资源的操作,进入内核态需要
资源限制 : I/O 数据库,cpu核数
死锁 :等待不到需要的资源
volatile
内存语义
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存中。当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量
硬件实现
使用硬件指令当前缓存行刷入主内存
是其他缓存中此变量的缓存行无效
使得读操作需要重新从主内存加载此变量
适用场景
只有一个线程对volatile变量写
Synchronized
锁的对象
Java中的每一个对象都可以作为锁。
对于同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前对象的Class对象。
对于同步方法块,锁是Synchonized括号里配置的对象
实现
同步方法
使用 ACC_SYNCHRONIZED 标记符隐示的实现,原理是通过方法调用指令检查该方法在常量池中是否包含 ACC_SYNCHRONIZED 标记符,JVM 要求线程在调用之前请求锁
同步代码块
JVM通过monitorenter和monitorexist指令实现同步锁的获取和释放功能
monitorenter指令是在编译后插入到同步代码块的开始位置
monitorexit指令是插入到方法结束处和异常处
JVM要保证每个monitorenter必须有对应的monitorexit与之配对
任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态
线程执行monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁
线程执行monitorexit指令时,将会将进入次数-1直到变成0时释放监视器
同一时刻只有一个线程能够成功,其它失败的线程会被阻塞,并放入到同步队列中,进入BLOCKED状态
虚拟机做的锁优化
1.锁消除,消除无谓的锁
2.锁粗化,合并太小粒度的加锁
3.锁自旋,自适应自旋
4.锁膨胀
锁膨胀
Java对象头
偏向锁
为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径
,而偏向锁则是在只有一个线程执行同步块时进一步提高性能
轻量级锁
目的是没有多线程竞争的前提下,减少传统的重量级锁
轻量级锁是为了在线程交替执行同步块时提高性能
重量级锁实现
Monitor Record结构
MonitorRecord(统一简称MR)是Java线程私有的数据结构,每一个线程都有一个可用MR列表,同时还有一个全局的可用列表
一个被锁住的对象都会和一个MR关联(对象头的MarkWord中的LockWord指向MR的起始地址)
MR中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用
Monitor Record工作机理
线程如果获得监视锁成功,将成为该监视锁对象的拥有者
作者:山鱿鱼
链接:https://www.jianshu.com/p/bdbd14e709da