0%

java | synchronized 优化 自旋优化

重量级锁竞争的时候,还可以使用自旋来进行优化。

如果当前进程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞「阻塞需要上下文切换,降低速度」。

自旋重试成功

线程 1(CPU 1 上) 对象 Mark 线程2(CPU2 上)
- 10(重量锁) -
访问同步块,获取 monitor 10(重量锁) 重量锁指针 -
成功(加锁) 10(重量锁) 重量锁指针 -
执行同步块 10(重量锁) 重量锁指针 -
执行同步块 10(重量锁) 重量锁指针 访问同步块,获取 monitor
执行同步块 10(重量锁) 重量锁指针 自旋重试
执行完毕 10(重量锁) 重量锁指针 自旋重试
成功(解锁) (无锁)) 自旋重试
- 10(重量锁) 重量锁指针 成功(加锁)
- 10(重量锁) 重量锁指针 执行同步块
-

怎么理解上述含义呢?

  • 线程 1 进行了同步块的加锁
  • 线程 2 也想用同步块,但是,它没有看到有人加锁后,就进入阻塞阶段,而是,不断的询问锁是否释放
    • 因为切换到阻塞,是切换上下文,消耗资源
  • 如果锁释放了,则它进行同步块加锁

所以,自旋优化只有多核 CPU 的时候才有意义,单核 CPU ,都是顺序执行,老是重复问没有意义。

自旋重试失败

线程 1(CPU 1 上) 对象 Mark 线程2(CPU2 上)
- 10(重量锁) -
访问同步块,获取 monitor 10(重量锁) 重量锁指针 -
成功(加锁) 10(重量锁) 重量锁指针 -
执行同步块 10(重量锁) 重量锁指针 -
执行同步块 10(重量锁) 重量锁指针 访问同步块,获取 monitor
执行同步块 10(重量锁) 重量锁指针 自旋重试
执行完毕 10(重量锁) 重量锁指针 自旋重试
执行完毕 10(重量锁) 重量锁指针 自旋重试
执行完毕 10(重量锁) 重量锁指针 阻塞
-
  • JAVA 6 之后,自旋锁是自适应的,比如对象刚刚一次自旋操作成功过,那么,认为这次自旋成功的可能性会高,就多旋几次;反之,就少自旋身之不自旋
  • 自旋会占用 CPU 时间,单核 CPU 自旋就是浪费
  • JAVA7 之后不能控制是否开启自旋功能
请我喝杯咖啡吧~