Java关键字synchronized

news2024/11/19 18:26:40

提纲

alt

定义

synchronized是同步块,实现了多线程间的互斥同步。它修饰的代码,确保任一时刻只有一个线程进入访问。

特性

因为在synchronized同步块内,只有一个线程能访问,因此确保了同步块内的原子性、可见性和有序性。

使用方式

alt 总结:

Class tClass = T.class; // T.class其实就是该类的类对象

synchronized不管是修饰代码块还是修饰方法,本质都是作用于对象上。进入代码块时需要获取对象锁,退出同步块是释放对象锁。

synchronized底层实现原理

alt Java对象锁的信息存在Java对象头里的mark word中。
synchronized不管是修饰代码块还是修饰方法,都能确定一个对象与之关联监视器。
对象监视器(ObjectMonitor)是在jdk中使用c++实现的,具体细节需阅读对应源码。

synchronized vs ReentrantLock

alt 总结:

  1. >=JDK1.6后, jvmsynchronized关键字的锁做了很多优化,其性能和 ReentrantLock的Api式锁相差无几;不过新的api的锁支持3个高级特性。
  2. ReentrantLock的底层实现是基于 AQS的; synchronizedjvm基于字节码 monitorentermonitorexit加上一些锁优化实现的。

提高锁性能

减少锁持有时间

alt

减小锁粒度

JDK1.7ConcurrentHashMap实用了分段锁来减小锁粒度(缩小锁对象的范围),从而降低锁冲突的可能性,进而提高系统的并发能力。

读写分离替换独占锁

在读多写少的场合使用读写锁可以有效提升系统的并发能力。

锁分离

锁分离是读写锁的进一步延伸,读写锁是根据读写操作上的不同,对锁进行了有效的分离。 在其他角度的分离思想,也可以对独占锁进行分离。 比如LinkedBlockingQueue的实现,其中take()put()分别实现了从队列中获取数据和往队列中增加数据的功能,将独占锁分离为头锁和尾锁能提升take()put()的并发能力。 alt

锁优化

<=JDK1.5时,synchronized直接就是重量级锁,所以性能不好。在JDK1.6版本中,平台对这部分的锁性能做了很多优化,例如锁消除、锁粗化、偏向锁、自适应自旋、轻量级锁等优化。

锁消除

alt 低于JDK1.5版本,编译器会将+号连接字符串的代码优化为StringBuffer的连续append()操作;然后即时编译器会对代码做“逃逸分析”发现sb不会超出方法外,因此会将append方法内的同步完全消除掉执行,提高效率。

锁粗化

alt 虚拟机在遇到一连串连续地对同一个锁不断进行请求和释放的操作时,便会把所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步次数,这个操作叫做锁粗化。

偏向锁

优化思想: 如果一个线程获得了锁(通过CAS将当前线程指针记录到mark word中),那么锁就进入偏向模式,当该线程再次请求锁时,不需要做任何同步操作。

适用场景: 对于没有任何锁竞争的场合,偏向锁优化效果好。 在锁竞争激烈的场景,如果每次来请求锁的线程都是不同线程,那么偏向模式会失效。

JVM配置参数: -XX:+UseBiasedLocking 开启偏向锁优化。 -XX:BiasedLockingStartupDelay=4 偏向锁延迟启动,默认4秒 。

轻量级锁

如果偏向锁失败,虚拟机会尝试轻量级锁的优化手段。

优化思想: 对于绝大部分的锁,在整个同步周期内都是不存在竞争的。(这是一个经验数据)

如果没有竞争,轻量级锁使用CAS操作避免使用互斥量的重量级锁开销。 单如果有竞争,CAS和互斥量开销都有,因此在有竞争的情况下,轻量级锁比重量级锁更慢。

实现: 在同步对象没有被锁定(锁标志位为01状态),虚拟机会在当前线程的栈中建立锁记录(Lock Record)的空间,然后通过CAS将对象的Mark Word对应位存储为锁记录的指针。如果成功,则说明获取轻量级锁成功,并更新该对象的Mark Word的锁标志位为00。

如果有2条以上线程争用同一个锁,则轻量级锁失效,会执行锁升级过程。

自旋&自适应自旋

如果轻量级锁失败,虚拟机还会做最后的尝试(自旋的优化)。

优化思想: 当前线程暂时无法获得锁,也许在几个CPU时钟周期后就可以获得锁。因此先不挂起线程,而是让线程做几个空循环后,如果获取到锁则进入临界区(还是轻量级锁状态);如果还是没有获取到锁,就膨胀为重量级锁。

JVM配置参数: -XX:+UseSpinning 开启自旋锁,JDK1.4.2已经引入,默认关闭。在JDK1.6之后默认开启。 -XX:PreBlockSpin=10 自旋次数,默认10次。后面加入自适应自旋后该参数无效。

自适应自旋: 手动设置自旋次数其实是不合理的,所以程序会根据前一次在同一个锁上的自旋时间及锁的拥有者状态来决定。

  • 如果上一次刚刚成功通过自旋获取过锁,且持有锁的线程正在运行中,虚拟机会认为这次自旋也很有可能成功,进而允许更长时间的自旋等待。
  • 如果对于某个锁,自旋很少成功过,则虚拟机会省略自旋获取锁的过程,避免浪费处理器资源。

锁升级

alt

详细流程如下图: alt

例子:

/**
 * 锁升级测试 jdk版本=1.8
 * -XX:+UseBiasedLocking 默认1.6之后就开启了偏向锁
 * -XX:BiasedLockingStartupDelay=5  偏向锁启动延迟,单位秒,系统默认值是4
 *
 * 结论:
 *  当不开启偏向锁时,能得到 001(无锁) -> 000(轻量级锁) -> 010(重量级锁)
 *  开启偏向锁时,并设置延时5秒,new之后sleep 6秒,for循环内1个线程,则能得到 001(无锁) -> 000(轻量级锁) -> 101(偏向锁)
 *  开启偏向锁时,并设置延时5秒,new之后sleep 6秒,for循环内2个线程及以上,则能得到 001(无锁) -> 000(轻量级锁) -> 010(重量级锁)
 */
public class LockUpTest {
    // 锁对象
    private static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {

        // new状态 -- 001
        System.out.println(Thread.currentThread().getName() + " -- " + ClassLayout.parseInstance(lock).toPrintable());
        Thread.sleep(6000);

        synchronized (lock){
            // 轻量级锁 -- 000
            System.out.println(Thread.currentThread().getName() + " -- " + ClassLayout.parseInstance(lock).toPrintable());
        }

        // 偏向锁 -- 101
        Object newLock = new Object();
        new Thread(()->{
            synchronized (newLock) {
                System.out.println(Thread.currentThread().getName() + " -- " + ClassLayout.parseInstance(newLock).toPrintable());
            }
        }).start();

        // 重量级锁 -- 010(当线程数大于1)
        for(int i=0;i<2;i++){
            new Thread(()->{
                synchronized (lock) {
                    System.out.println(Thread.currentThread().getName() + " -- " + ClassLayout.parseInstance(lock).toPrintable());
                }
            }).start();
        }
    }
}

输出: alt

参考资料

  • 书籍 周志明 * 《深入理解Java虚拟机》
  • 书籍 葛一鸣 * 《Java高并发程序设计》
  • 网上文章 - https://www.cnblogs.com/Alei777/p/16223842.html

本文由 mdnice 多平台发布

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

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

相关文章

SpringBoot 统⼀功能处理 AOP

接下来是 Spring Boot 统⼀功能处理模块了&#xff0c;也是 AOP 的实战环节&#xff0c;要实现的⽬标有以下 3 个&#xff1a; 统⼀⽤户登录权限验证&#xff1b;统⼀数据格式返回&#xff1b;统⼀异常处理。 1.⽤户登录权限效验 ⽤户登录权限的发展从之前每个⽅法中⾃⼰验…

day35【代码随想录】贪心算法之加油站、分发糖果、柠檬水找零

文章目录前言一、加油站&#xff08;力扣134&#xff09;方法一方法二二、分发糖果&#xff08;力扣135&#xff09;三、柠檬水找零&#xff08;力扣860&#xff09;前言 1、加油站 2、分发糖果 3、柠檬水找零 一、加油站&#xff08;力扣134&#xff09; 在一条环路上有 n 个…

好文推荐!LLM技术精要;美图发全员激励股✦票;百度/微信大会精华笔记;Flink商✦业化再起波澜;GitHub今日热榜 | ShowMeAI资讯日报

&#x1f440;日报合辑 | &#x1f3a1;AI应用与工具大全 | &#x1f514;公众号资料下载 | &#x1f369;韩信子 &#x1f3a1; 『通向 AGI 之路』大型语言模型&#xff08;LLM&#xff09;技术精要 实话实说&#xff0c;国内在 LLM 模型相关技术方面&#xff0c;此刻距离最先…

【Linux】基础开发工具使用 --- gcc

目录 预处理 编译 汇编 链接 函数库 协助记忆 &#x1f9cb;GCC&#xff08;GNU Compiler Collection&#xff09;是由GNU开发的编程语言编译器。GNU编译器套件包括C、C、 Objective-C、 Fortran、Java、Ada和Go语言前端&#xff0c;也包括了这些语言的库&#xff08;如l…

Docker tarsgo

目录 参考&#xff1a; mysql镜像安装 一、安装镜像 二、创建mysql容器 使用 tarscloud/framework 部署框架 拉取最新版本镜像 启动镜像(目前只考虑了 linux 上, 时间和本机同步) 目录说明 参数解释 Docker 部署 Tars 应用节点 开发环境 docker-compose go安装 ubu…

CSS自定义滚动条

大家好&#xff0c;我是半夏&#x1f474;&#xff0c;一个刚刚开始写文的沙雕程序员.如果喜欢我的文章&#xff0c;可以关注➕ 点赞 &#x1f44d;&#xff5e; 搞前端的半夏 一起学习交流前端&#xff0c;成为更优秀的前端工程师 前言 之前写过一篇scroll-snap让你的滚动条更…

《c++ primer笔记》第二章 变量和基本类型

前言 最近开始二刷c primer&#xff0c;第一遍很模糊的过了一下&#xff0c;由于前面的基础很多没理解透&#xff0c;从12章到16章基本是懵逼的状态。第二次为了保证质量准备把每个章节个人感觉重要的部分进行一个记录与总结&#xff0c;其中也记录了部分看书过程中遇到的问题&…

[JS]JavaScript基础学习笔记(黑马pink+尚硅谷李立超)

文章目录&#x1f97d; 前言&#x1f97d; JavaScript 简介&#x1f30a; JavaScript 是什么&#x1f30a; JavaScript 的作用&#x1f30a; HTML/CSS/JS 的关系&#x1f30a; 浏览器执行 JS&#x1f30a; JS 的组成&#x1f97d; JavaScript 的书写位置&#x1f30a; 行内式 J…

【C语言】指针进阶(一)

学好指针✊✊✊还有&#xff0c;男孩子在外面要保护好自己一、字符指针字符也有地址&#xff0c;当然可以将其储存——字符指针&#xff0c;是储存字符地址的指针对于普通的单个字符&#xff1a;char ch a;char* pc1 &ch;这里的pc是单个变量ch‘&#xff08;单个字符&…

防火门监控系统在智能建筑消防的重要性及应用介绍

【摘要】&#xff1a; 安全、舒适的生活及办公环境是人们所追求的&#xff0c;因此&#xff0c;在建筑中各种智能化的设备及布控系统显得尤为重要。近年各种频发的高危火灾事件严重威胁到了国民的生命安全&#xff0c;所以火灾监控系统在建筑中的应用显得尤为重要。本文主要从智…

【涵子来信python大全】——第二季——opencv第一篇

各位亲爱的读者&#xff0c;博主&#xff1a; 首先恭喜大家&#xff0c;涵子来信已经到达第二季——2023年篇。今天&#xff0c;我们要步入机器学习的初级内容&#xff1a;python opencv图片&#xff01; 目录 一、提前准备 二、程序代码学习 2.1.如何读取图片 2.2.显示图…

MySQL架构,以及redo log、undo log和binlog的区别(六)

一、Mysql的基本架构图 二、连接器 连接器负责跟客户端建立连接&#xff0c;获取权限、维持和管理连接&#xff1a; 用户名密码验证&#xff1b;查询权限信息&#xff0c;分配对应的权限&#xff1b;可以使用show processlist查看现在的连接&#xff1b;如果太长时间没有动静…

【rt-thread网络】第0篇:使用paho-mqtt软件包连接腾讯云mqtt服务器

文章目录一、mqtt介绍二、paho mqtt介绍三、连接腾讯云的步骤3.1 在腾讯云控制台的IOT HUB创建产品和设备&#xff08;略&#xff09;3.2 根据产品信息填充MQTTClient的连接参数3.3 编译和下载到开发板&#xff08;略&#xff09;四、测试五、参考一、mqtt介绍 MQTT(消息队列遥…

【Linux操作系统】如何实现Linux中软件安装进度条?

文章目录一.回车与换行二.缓冲区问题三.倒计时小程序四.进度条小程序Linux下安装软件时&#xff0c;经常会看到类似上图的进度条&#xff0c;今天带大家用C语言来演示其原理&#xff01; 一.回车与换行 俗话&#xff1a;回车换行&#xff0c;实际是回车和换行的组合 回车是回…

助力工业物联网,工业大数据项目之数据采集

文章目录01&#xff1a;Sqoop命令回顾02&#xff1a;YARN资源调度及配置03&#xff1a;MR的Uber模式04&#xff1a;Sqoop采集数据格式问题05&#xff1a;问题解决&#xff1a;Avro格式06&#xff1a;Sqoop增量采集方案回顾01&#xff1a;Sqoop命令回顾 目标&#xff1a;掌握Sqo…

TypeScript环境搭建 下载/安装 ,编译运行的三种方式:tsc命令行/tsc-node库/webpack搭建环境

目录 什么是TypeScript? 首先来进行全局安装 &#xff1a; 编译运行 方式一&#xff1a;命令行(cmd终端)--->tsc命令行 1.将代码编译为JavaScript的代码&#xff0c;使用cmd终端或者命令行运行以下命令&#xff1a; 2.在浏览器或者Node环境下运行JavaScript代码 方式…

声纹识别与声源定位(一)

针对目前智能计算机及大规模数据的发展&#xff0c;依据大脑处理语音、图像数据方法的deep learning技术应运而生。deep learning技术是应用于音频信号识别&#xff0c;模仿大脑的语音信号学习、识别的模式。在音频信号处理的过程中&#xff0c;运用deep learning进行音频数据的…

极海APM32F072RB开发环境测试

极海APM32F072RB开发环境测试通过自制的开发板进行测试。 &#x1f3ac;基于STM32cubemx工程配置 Keil MDK编译 ST-LINK/V2烧录 &#x1f33b;基于APM32F0xx_SDK Keil MDK编译 ST-LINK/V2烧录 &#x1f33f;官方的SDK包下载地址&#xff1a;https://www.geehy.com/support/…

DMDW主备集群搭建备库先open引发的问题

一、问题描述及配置主备集群搭建成功后&#xff0c;主备库启动脚本中START_MODEmount&#xff0c;备库的lsn号大于等于备库&#xff0c;N_OPN打开次数主库大于备库。假如搭建主备集群后&#xff0c;备库首先OPEN一下后引发的问题如下图&#xff1a;启动脚本中START_MODEopen3、…

tcp紧急指针,mss,rto,零窗口探测等

三次握手、四次挥手、重传机制、滑动窗口、流量控制、拥塞控制、TCP/UDP全解析-蒲公英云 (dandelioncloud.cn)(511条消息) TCP零窗口探测_redwingz的博客-CSDN博客_tcp0窗口TCP系列32—窗口管理&流控—6、TCP zero windows和persist timer - 走看看 (zoukankan.com)TCP协议…