Java中的锁
宏观分类:
| 名称 | 乐观锁 | 悲观锁 |
|---|---|---|
| 特点 | 读多写少, 遇到并发写的可能性低 | 认为写多,遇到并发写的可能性高 |
| 上锁时机 | 读不上锁,更新时,上锁 | 读写都上锁 |
| 实现 | CAS【读-比较-写】 | CAS乐观锁—转换—> 悲观锁ReentrantLock |
| 代表类型 |
细分:
| 偏向锁 | 自旋锁 | 轻量级锁 | 重量级锁 |
|---|---|---|---|
| 特点 | 偏向于第一个访问锁的线程,并存储那个线程ID | ||
| 加锁 | |||
| 解锁 | |||
| 实现 | 1.访问Mark Word中偏向锁的标识是否设置成1,锁标志位是否为01,确认为可偏向状态。 2.如果为可偏向状态,则测试线程ID是否指向当前线程,如果是,进入步骤5,否则进入步骤 3. 如果线程ID并未指向当前线程,则通过CAS操作竞争锁。如果竞争成功,则将Mark Word中线程ID设置为当前线程ID,然后执行5;如果竞争失败,执行 4. 如果CAS获取偏向锁失败,则表示有竞争。当到达全局安全点(safepoint)时获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码。(撤销偏向锁的时候会导致stop the word) 5.执行同步代码。 | 1.在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,官方称之为 Displaced Mark Word。 拷贝对象头中的Mark Word复制到锁记录中; 拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock record里的owner指针指向object mark word。如果更新成功,则执行步骤4,否则执行步骤5。 如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,即表示此对象处于轻量级锁定状态,这时候线程堆栈与对象头的状态如图所示。 如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。 而当前线程便尝试使用自旋来获取锁,自旋就是为了不让线程阻塞,而采用循环去获取锁的过程。 | |
| 优点 | 避免用户线程和内核的切换的消耗。 | ||
| 缺点 | 线程自旋是需要消耗cup的,说白了就是让cup在做无用功 如果一直获取不到锁,那线程也不能一直占用cup自旋做无用功,所以需要设定一个自旋等待的最大时间。 占着XX不XX,同时有大量线程在竞争一个锁,会导致获取锁的时间很长,线程自旋的消耗大于线程阻塞挂起操作的消耗,其它需要cup的线程又不能获取到cpu,造成cpu的浪费 | ||
| 改进 | 自旋时间阈值 |
170万+

被折叠的 条评论
为什么被折叠?



