当前位置: 首页 > news >正文

synchronized的详解、锁的升级过程和优缺点比较

本文 详细介绍Java中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁轻量级 锁、重量级锁,以及锁升级过程。

Java中每一个对象都可以作为锁。具体表现形式为以下三种形式:

  • 对于普通的同步方法,锁是当前的实例对象
  • 对于静态同步方法,锁是当前类的Class对象
  • 对于同步方法块,锁是Synchronized括号里面的配置对象

演示:

Person类中的代码:

package com.qcby.demo1;public class Person {public int age;public String name;public int[] arr = {1,2,3};public String[] arr2 = {"aaa","bbb"};public Person son;//锁对象public synchronized void m1(){System.out.println("我是m1开始");try{Thread.sleep(4000);}catch(InterruptedException e){}System.out.println("我是m1结束");}//锁对象public  synchronized void m2(){System.out.println("我是m2开始");try{Thread.sleep(4000);}catch(InterruptedException e){}System.out.println("我是m2结束");}//锁类public synchronized static void m3(){System.out.println("我是m3开始");try{Thread.sleep(4000);}catch (InterruptedException e){}System.out.println("我是m3结束");}//锁类public synchronized static void m4(){System.out.println("我是m4开始");try{Thread.sleep(4000);}catch (InterruptedException e){}System.out.println("我是m4结束");}}

Test类中的代码:

package com.qcby.demo1;public class Test {public static void main(String[] args) {System.out.println("阿瑟东");Person ww = new Person();Thread x1 = new Thread() {@Overridepublic void run(){ww.m1();}};Thread x2 = new Thread() {@Overridepublic void run(){ww.m2();}};Thread x3 = new Thread(){@Overridepublic void run(){ww.m3();}};Thread x4 = new Thread(){@Overridepublic void run(){ww.m4();}};x1.start();x2.start();x3.start();x4.start();}}

结果展示:

1、调用同一个对象中的m1、m2方法时:

m2方法会等待m1方法结束之后才能开启

2、调用不同对象中的m1、m2方法时:

m1和m2方法几乎同时开启,同时结束

3、调用同一个类中的m3、m4方法时:

m4方法会等待m3方法结束之后才能开启

锁的升级和对比:

偏向锁

大多数情况下,锁不仅不存在多线程竞争,而且总是由同 一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁

当一个线程访问同步块并且获取锁的时候,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块的时候不需要进行CAS操作来加锁和解锁,只需要测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。

  • 如果测试成功,表示线程已经获得了锁
  • 如果测试失败,则需 要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁)
    • 如果没有设置,则 使用CAS竞争锁;
    • 如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。

偏向锁的撤销:偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时, 持有偏向锁的线程才会释放锁。

轻量级锁

当两个或以上的线程交替获取锁,但并没有在对象上并发的获取锁时,偏向锁升级为轻量级锁。在此阶段,线程采取CAS的自旋方式尝试获取锁,避免阻塞线程造成CPU在用户态和内核态间转换的消耗。

重量级锁

因为自旋会消耗CPU,为了避免无用的自旋(比如获得锁的线程被阻塞住了),一旦锁升级 成重量级锁,就不会再恢复到轻量级锁状态。当锁处于这个状态下,其他线程试图获取锁时, 都会被阻塞住,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程就会进行新一轮 的夺锁之争。

三种锁的优缺点的对比:

优点

缺点

适用场景

偏向锁

加锁和不加锁不需要额外的消耗,和事项非同步方法相比只存在纳秒级别的差距。

如果线程之间存在锁的竞争,会带来额外的锁撤销的消耗

适用于只有一个线程访问同步块场景

轻量级锁

竞争的线程不会阻塞,提高了程序的响应速度

如果一直得不到锁竞争的线程,适用自旋回消耗CPU

追求响应时间,同步块执行速度非常快

重量级锁

线程竞争不适用自旋,不会消耗CPU

线程阻塞,响应时间缓慢

追求吞吐量,同步块执行速度比较长


http://www.mrgr.cn/news/27674.html

相关文章:

  • Spring Boot 多环境开发配置详解:Profiles 的使用指南
  • 探索Python编程:从入门到进阶的奇妙之旅
  • 【分布式】CAP理论
  • Docker+Django项目部署-从Linux+Windows实战
  • 1、C语言学习专栏介绍
  • 图形几何之美系列:法向量计算之轮廓有向面积辅助法
  • 什么是快充协议,最常见的快充协议有哪些
  • 进程间通信之消息队列详解
  • 个人虚拟物品商城网站源码,后台试Thinkphp6.0开发的,前端是vue的。
  • 三、(JS)JS中常见的表单事件
  • 返回当前栈内最小元素
  • Dubbo SPI源码
  • 面试题总结(三) -- 内存管理篇
  • Java--图书管理系统(新版详细讲解)
  • Scrapy 2.6 Spider Middleware 爬虫页中间件基本使用
  • 基于python+django+vue的学生考勤管理系统
  • 86-java jmap分析内存
  • Java API 之集合框架进阶
  • 24年云南省下半年事业单位少有人知的10个真相
  • 【Android Studio】API 29(即Android 10)或更高版本,在程序启动时检查相机权限,并在未获取该权限时请求它
  • 约瑟夫环和一元多项式修正版
  • 乌俄冲突下AI和计算机的使用
  • protobuf中c、c++、python使用
  • 基于SSM的二手车管理系统的设计与实现 (含源码+sql+视频导入教程)
  • 【C#生态园】深度剖析:C#嵌入式开发工具大揭秘
  • [JVM]JVM内存划分, 类加载过程, 双亲委派模型,垃圾回收机制