JAVA:深入了解Java中的Synchronized关键字

news2024/9/20 1:10:39

1、简述

在Java中,多线程编程是一项常见的任务,然而,它也伴随着一系列潜在的问题,比如竞态条件(Race Condition)和数据不一致性。为了解决这些问题,Java提供了一种同步机制,即synchronized关键字。本文将深入探讨Java中synchronized技术,介绍它的基本概念、用法和一些最佳实践。

2、关键特性

synchronized关键字可以保证并发编程的三大特性:原子性、可见性、有序性,而volatile关键字只能保证可见性和有序性,不能保证原子性,也称为是轻量级的synchronized。

  • 原子性:一个或多个操作全部执行成功或者全部执行失败。synchronized关键字可以保证只有一个线程拿到锁,访问共享资源。
  • 可见性:当一个线程对共享变量进行修改后,其他线程可以立刻看到。 执行synchronized时,会对应执行lock、unlock原子操作,保证可见性。
  • 有序性:程序的执行顺序会按照代码的先后顺序执行。

synchronized关键字可以实现什么类型的锁?

  • 悲观锁:synchronized关键字实现的是悲观锁,每次访问共享资源时都会上锁。

  • 非公平锁:synchronized关键字实现的是非公平锁,即线程获取锁的顺序并不一定是按照线程阻塞的顺序。

  • 可重入锁:synchronized关键字实现的是可重入锁,即已经获取锁的线程可以再次获取锁。

  • 独占锁或者排他锁:synchronized关键字实现的是独占锁,即该锁只能被一个线程所持有,其他线程均被阻塞。

3、基本使用

3.1 synchronized关键字的基本概念

synchronized是Java中用于实现同步的关键字,它可以用来修饰方法或代码块。当一个线程进入一个由synchronized修饰的方法或代码块时,它将自动获得锁,其他线程必须等待直到该线程释放锁。这确保了在同一时刻只有一个线程可以执行被synchronized修饰的代码。

public synchronized void synchronizedMethod() {
    // 同步的代码块
}
3.2 对象级别的锁和类级别的锁

在synchronized中,锁可以是对象级别的,也可以是类级别的。对象级别的锁是基于对象实例的,而类级别的锁是基于类的Class对象的。

// 对象级别的锁
public synchronized void objectLevelLock() {
    // 同步的代码块
}

// 类级别的锁
public static synchronized void classLevelLock() {
    // 同步的代码块
}
3.3 同步代码块

除了修饰方法,synchronized还可以用于同步代码块。这使得我们可以更加精细地控制同步的范围,提高程序的性能。

public void someMethod() {
    // 非同步的代码

    synchronized (lockObject) {
        // 需要同步的代码块
    }

    // 非同步的代码
}

4、实现原理

Synchronized的底层实现原理是完全依赖JVM虚拟机的,所以谈synchronized的底层实现,就不得不谈数据在JVM内存的存储:Java对象头,以及Monitor对象监视器。
在这里插入图片描述
以下我们来看一下Monitor的实现原理:

ObjectMonitor() {
    _header       = NULL;
    _count        = 0;  //锁的计数器,获取锁时count数值加1,释放锁时count值减1
    _waiters      = 0,  //等待线程数
    _recursions   = 0;  // 线程重入次数
    _object       = NULL;  // 存储Monitor对象
    _owner        = NULL;  // 持有当前线程的owner
    _WaitSet      = NULL;  // wait状态的线程列表
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;  // 阻塞在EntryList上的单向线程列表
    FreeNext      = NULL ;
    _EntryList    = NULL ;  // 处于等待锁状态block状态的线程列表
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
  }
4.1 运行流程

通过以下代码添加同步锁来解析当前线程运行流程:

public  void synch1(){
    synchronized(this){
        try {
            synchronized (this){
                TimeUnit.MINUTES.sleep(2);
            }
            System.out.println(Thread.currentThread().getName()+" is runing");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

通过JAR 自带的jconsole.exe 我们可以很清楚的看到当前运行的6个线程状况:
在这里插入图片描述
也可以通过Java自带的Jstack + PID 来查看线程运行情况:

jstack 21836

在这里插入图片描述

4.2 如何加锁

首先我们来看看对方法同步的运行情况,可以通过反编译class类来查看当前Java代码运行的情况:

public synchronized static void synch0(){
    try {

        TimeUnit.SECONDS.sleep(2);
        System.out.println(Thread.currentThread().getName()+" is runing");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

通过Javap指令来解析当前class类:

javap -v SynchronizedDemo

找到反编译指定的代码块,我们可以很清楚的看到同步的方法,是通过flags的ACC_SYNCHRONIZED来实现加锁和解锁的:
在这里插入图片描述

接下来我们对代码块实现加Synchronized操作:

public  void synch1(){
    synchronized(this){
        try {
            synchronized (this){
                TimeUnit.MINUTES.sleep(2);
            }
            System.out.println(Thread.currentThread().getName()+" is runing");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

同理反编译,我们可以很清楚的看到同步代码块是通过Monitorenter和Monitorexit来实现同步加锁的:
在这里插入图片描述
小结:Java虚拟机是通过进入和退出Monitor对象来实现代码块同步和方法同步的,代码块同步使用的是monitorenter和 monitorexit 指令实现的,而方法同步是通过Access flags后面的标识来确定该方法是否为同步方法

5. 锁优化

在Java多线程编程中,synchronized关键字是一种常见的同步机制,用于保护共享资源,防止多个线程同时访问而导致数据不一致的问题。
jDK对synchronized锁进行升级:

5.1 偏向锁(Biased Locking)

偏向锁是为了解决大多数情况下都是由同一线程多次获得锁的场景。当一个线程第一次访问一个同步代码块时,JVM会将对象头的Mark Word设置为偏向锁,并将线程ID保存其中。之后,该线程再次进入同步代码块时,无需再进行同步操作,直接获得锁。

这种优化避免了多线程竞争的开销,提高了单线程执行同步代码块的性能。

5.2 轻量级锁(Lightweight Locking)

当多个线程竞争同一个锁时,偏向锁会失效,这时JVM使用轻量级锁进行优化。轻量级锁的核心思想是,使用CAS操作尝试在对象头的Mark Word中存储锁记录,而不是直接争夺锁。

第一个获得锁的线程会在对象头中记录锁信息,后续线程通过CAS更新锁记录。如果CAS成功,线程获得锁;否则,升级为重量级锁。

5.3 重量级锁(Heavyweight Locking)

在极端情况下,多个线程同时竞争同一个锁,轻量级锁无法解决竞争,JVM会将锁升级为重量级锁。这时,JVM使用操作系统提供的互斥量来保证同一时刻只有一个线程能够获得锁。

虽然重量级锁提供了最强的同步性,但相对于偏向锁和轻量级锁,它的性能开销较大。

6、结论

synchronized是Java中用于解决多线程同步问题的基本机制之一。通过深入理解synchronized的基本概念、对象级别的锁和类级别的锁、同步代码块、避免死锁以及性能考虑,我们可以更好地编写安全且高效的多线程程序。在实际应用中,根据具体场景选择合适的同步机制是至关重要的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1305408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目一:IIC读写EEPROM AT24C02

回头想了想在工作中调过的EEPROM还挺多的,有M24M02 、M28010 、AT24C02等,今天讲一下AT24C02吧 一、AT24C02简介 1.1 特点 文档已经上传了,需要的同学可以自行下载哈,晚点我会把下载链接附上来。 我大概照着文档翻译了一下&am…

总线一:I2C简介(介绍看这一篇就够啦)

本节主要介绍以下内容: I2C协议简介 STM32的I2C特性及架构 I2C初始化结构体详解 一、I2C协议简介 I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强&#xff…

2000-2021年全国各省环境规制水平数据

2000-2021年全国各省环境规制水平数据 1、时间:2000-2021年 2、范围:30省市 3、指标:工业污染治理完成投资、工业增加值、环境规制强度 4、计算说明:环境规制工业污染治理完成投资/工业增加值 5、来源:国家统计局…

LLM之RAG理论(一)| CoN:腾讯提出笔记链(CHAIN-OF-NOTE)来提高检索增强模型(RAG)的透明度

论文地址:https://arxiv.org/pdf/2311.09210.pdf 检索增强语言模型(RALM)已成为自然语言处理中一种强大的新范式。通过将大型预训练语言模型与外部知识检索相结合,RALM可以减少事实错误和幻觉,同时注入最新知识。然而&…

Linux - 进程间通信(中)- 管道的应用场景

前言 在上篇博客当中,对Linux 当中的进程通信,做了详细阐述,主要是针对父子进程的通信来阐述的同时,也进行了模拟实现。 对于管道也有了初步了解,但是这仅仅是 进程间通信的一部分,Linux 当中关于进程间通…

散点图,盒须图,折线图混放在一个echarts

散点图,何须图,折线图混放在一个echarts option {tooltip: {trigger: axis,axisPointer: {type: cross,crossStyle: {color: #999}}},legend: {data:[盒须图1,盒须图2,折线图,散点图]},xAxis: [{type: category,data: [周一,周二,周三,周四,周五,周六…

智能优化算法应用:基于萤火虫算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于萤火虫算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于萤火虫算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.萤火虫算法4.实验参数设定5.算法结果6.参考文…

官宣 | HelpLook已入驻企业微信应用市场

HelpLook正式入驻企业微信第三方应用市场。 HelpLook支持自定义域名与AI站内搜索,能够帮助企业微信用户搭建所见即所得的企业知识库、产品帮助中心、用户手册、企业博客。 | 怎么找到HelpLook并开始使用 在企业微信的第三方应用就可直接搜索HelpLook,添…

mysql数据库损坏后重装,数据库备份

重装 先卸载 sudo apt-get remove --purge mysql-server mysql-client mysql-common sudo apt-get autoremove sudo apt-get autoclean 然后重新安装MySQL: sudo apt-get install mysql-server mysql-client 首先要先使用无密码登录数据库一定要使用 sudo mysql -uroo…

新手上路:盘点「性能测试」必须掌握的技术点

前段时间,有一些小伙伴提出希望我们推送点性能测试的技术干货。所以,小编今天通过上网查资料,结合项目实操过程中的一些问题,总结了一些关于性能测试的内容,希望是大家想要了解的内容哈。 1、性能测试的目的 首先&am…

分布式环境认证和授权-基于springboot+JWT+拦截器实现-实操+源码下载

1、功能概述? 1、当用户登录的时候,将用户的信息通过JWT进行加密和签名,并将JWT产生了token信息保存到当前浏览器的localStoragee中,即本地存储中。 2、当用户登录成功后,访问其他资源的时候,程序从localStorage中获…

linux(4):linux基础命令第三弹

在linux基础命令第二弹中http://t.csdnimg.cn/JPNYY我们讲了有关路径,创建目录和文件、文件夹,以及如何查看文件内容的问题,第三弹我们将学习有关文件操作和查找以及过滤关键字、展示文件字节,行数的命令,还有一个很重…

【程序员的自我修养04】目标文件生成可执行文件过程

绪论 大家好,欢迎来到【程序员的自我修养】专栏。正如其专栏名,本专栏主要分享学习《程序员的自我修养——链接、装载与库》的知识点以及结合自己的工作经验以及思考。编译原理相关知识本身就比较有难度,我会尽自己最大的努力,争…

.Net中的集合

所有的集合都是继承自IEnumerable。集合总体可以分为以下几类:关联/非关联型集合,顺序/随机访问集合,顺序/无序集合,泛型/非泛型集合,线程集合。 各集合类底层接口关系图 泛型与非泛型集合类的分析 泛型集合是类型安…

自动化测试基础知识:什么是自动化测试?需要学习哪些知识与工具!

1、自动化测试概念 自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程。通常, 在设计了测试用例并通过评审之后,由测 试人员根据测试用例中描述的规程一步步执行测试,得到实际结果与期望结果的比较。简言之,自动化测试…

【操作系统导论】比例份额调度

本文介绍一种 比例份额(proportional-share) 调度程序,也称为 公平份额(fair-share)。 彩票调度 简介 彩票调度 的基本思想: 每隔一段时间,都会举行一次彩票抽奖,以确定接下来应该…

【上海大学数字逻辑实验报告】六、时序电路

一、 实验目的 掌握同步二进制计数器和移位寄存器的原理。学会用分立元件构成2位同步二进制加计数器。学会在Quartus II上设计单向移位寄存器。学会在Quartus II上设计环形计数器。 二、 实验原理 同步计数器是指计数器中的各触发器的时钟脉冲输入端连接在一起,接…

做题总结 707. 设计链表

做题总结 707. 设计链表 leetcode中单链表节点的默认定义我的尝试正确运行的代码(java) leetcode中单链表节点的默认定义 class ListNode {int val;ListNode next;//无参public ListNode() {}//有参:1public ListNode(int val) {this.val val;}//有参:…

【项目小结】优点分析

一、 个人博客系统 一)限制强制登录 问题:限制用户登录后才能进行相关操作解决: 1)前端: ① 写一个函数用于判断登录状态,如果返回的状态码是200就不进行任何操作,否则Ajax实现页面的跳转操作…

Apollo配置发布原理解析

📫作者简介:小明java问道之路,2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化,文章内容兼具广度、深度、大厂技术方案,对待技术喜欢推理加验证,就职于…