2_并发编程同步锁(synchronized)

news2024/12/26 9:18:36

并发编程带来的安全性同步锁(synchronized)

1.他的背景

当多个线程同时访问,公共共享资源的时候,这时候就会出现线程安全,代码如:

public class AtomicDemo {
    int i=0;
    //排他锁、互斥锁
    public  void incr(){  //synchronized
        i++;    //i++最终3条指令 [线程安全问题中原子性]
    }
    public static void main(String[] args) throws InterruptedException {
        AtomicDemo ad=new AtomicDemo();
        Thread[] thread=new Thread[2];
        for (int i = 0; i <2 ; i++) {
            thread[i]=new Thread(()->{ for(int k=0;k<10000;k++) {   ad.incr();   } });
            thread[i].start();
        }
        thread[0].join();
        thread[1].join();
        System.out.println("Result:"+ad.i);
    }
}
//执行结果:17986,如果加上synchronized同步锁后结果为20000.
Result:17986

图片解析过程:

在这里插入图片描述

2.基本使用

synchronized有三种方式来加锁,不同的修饰类型,代表锁的控制粒度:

  1. 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
  2. 静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
  3. 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁
public class SynchronizedDemo {
    //修饰实例方法
    public synchronized void m1(){  }
    
    Object lock=new Object(); //在内存中会分配一个地址来存储
    public void m2(){
        //代码块
        synchronized (lock){ } //lock为锁对象,也表示控制锁的范围
    }
    //静态方法
    public synchronized static void m3(){}
}
3.注意事项

锁的范围: synchronized中的锁对象如果是,普通对象这为当前对象锁,如果是静态类为全局锁。

4.底层原理

4.1 synchronized是如何实现锁的,以及锁的信息是存储在哪里?锁的信息是存储在锁对象下Markword对象头里的

4.2 在Hotspot虚拟机中,对象在内存中的存储布局,可以分为三个区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)

在这里插入图片描述

4.3 mark-word:对象标记字段占4个字节,用于存储一些列的标记位,比如:哈希值、轻量级锁的标记位,偏向锁标记位、分代年龄等

在这里插入图片描述

4.5 偏向锁状态[默认情况下,偏向锁的开启是有个延迟,默认是4秒 -XX:BiasedLockingStartupDelay=0] 为什么这么设计呢?

因为JVM虚拟机自己有一些默认启动的线程,这些线程里面有很多的Synchronized代码,这些
Synchronized代码启动的时候就会触发竞争,如果使用偏向锁,就会造成偏向锁不断的进行锁的升级和撤销,效率较低.

5.技术关联性

关于Synchronized锁的升级
jdk1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。
锁主要存在四中状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。

这么设计的目的,其实是为了减少重量级锁带来的性能开销,尽可能的在无锁状态下解决线程并发问
题,其中偏向锁和轻量级锁的底层实现是基于自旋锁,它相对于重量级锁来说,算是一种无锁的实现

在这里插入图片描述

如果有线程去抢占锁,那么这个时候线程会先去抢占偏向锁 [也就是把markword的线程ID改为当前抢占锁的线程ID的过程] ----》

如果有线程竞争,这个时候会撤销偏向锁,升级到轻量级锁 --》

如有线程超过自旋,升级到重量级锁 [有线程超过10次自旋(-XX:PreBlockSpin参数配置),或者自旋线程数超过
CPU核心数的一般,在1.6之后,加入了自适应自旋Adapative Self Spinning. JVM会根据上次竞争的情况来自动控制自旋的时间]


轻量级锁的获取及原理

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>
    
//-------------------------------

public class Demo {
    Object o=new Object();
    public static void main(String[] args) {
        Demo demo=new Demo(); //o这个对象,在内存中是如何存储和布局的。
        System.out.println(ClassLayout.parseInstance(demo).toPrintable());
        synchronized (demo){
        	System.out.println(ClassLayout.parseInstance(demo).toPrintable());
        }
    }
}

结果:
    00 00 (00000001 00000000 00000000 00000000) (1)  无锁
    d5 02 (11011[000] 11110000 11010101 00000010) (47575256) 轻量锁
    

它的锁的标记是轻量级锁呢?

默认情况下,偏向锁的开启是有个延迟,默认是4秒。为什么这么设计呢?
因为JVM虚拟机自己有一些默认启动的线程,这些线程里面有很多的Synchronized代码,这些
Synchronized代码启动的时候就会触发竞争,如果使用偏向锁,就会造成偏向锁不断的进行锁的升级和
撤销,效率较低

偏向锁的获取及原理

通过下面这个JVM参数可以讲延迟设置为0.
-XX:BiasedLockingStartupDelay=0

public class Demo {
    Object o=new Object();
    public static void main(String[] args) {
        Demo demo=new Demo(); //o这个对象,在内存中是如何存储和布局的。
        System.out.println(ClassLayout.parseInstance(demo).toPrintable());
        synchronized (demo){
        	System.out.println(ClassLayout.parseInstance(demo).toPrintable());
        }
    }
}
结果:
00 00 (00000101 00000000 00000000 00000000) (5)   偏向锁
4a 03 (00000101 00110000 01001010 00000011) (55193605) 偏向锁

重量级锁的获取

public static void main(String[] args) {
    Demo testDemo = new Demo();
    Thread t1 = new Thread(() -> {
        synchronized (testDemo){
            System.out.println("t1 lock ing");
            System.out.println(ClassLayout.parseInstance(testDemo).toPrintable());
    	}
    });
    t1.start();
    synchronized (testDemo){
        System.out.println("main lock ing");
        System.out.println(ClassLayout.parseInstance(testDemo).toPrintable());
    }
}

结果:
8a 20 5e 26 (10001010 00100000 01011110 00100110) (643702922)   重量锁
8a 20 5e 26 (10001010 00100000 01011110 00100110) (643702922)   重量锁

6.CAS

就是比较并交换的意思。它可以保证在多线程环境下对于一个变量修改的原子性。
CAS的原理很简单,包含三个值当前内存值(V)、预期原来的值(E)以及期待更新的值(N)。

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

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

相关文章

Allins 官网正式上线,铭文赛道进入 AMM 交易时代

“Allins 正在通过全新的 AMM 方案为BRC20及多链铭文资产拓展 DeFi 场景&#xff0c;官网的全新上线意味着铭文资产的交易正式进入 AMM 时代。” 在 2023 年 1 月开始&#xff0c; Ordinals 协议的推出成为了铭文赛道发展的开端&#xff0c;并为比特币这类非图灵完备的生态&…

虾皮马来站点选品:在虾皮(Shopee)5个热门品类和市场特点

在虾皮&#xff08;Shopee&#xff09;马来西亚站点选择商品时&#xff0c;卖家应该考虑一些热门品类和市场特点&#xff0c;以确保他们的产品能够满足当地消费者的需求并取得良好的销售业绩。以下是在虾皮&#xff08;Shopee&#xff09;马来西亚站点销售商品时需要考虑的五个…

大模型实战营第二期——1. 书生·浦语大模型全链路开源开放体系

文章目录 1. 实战营介绍2. 书生浦语大模型介绍2.1 数据2.2 预训练2.3 微调2.4 评测2.5 部署2.6 智能体(应用) 1. 实战营介绍 github链接&#xff1a;https://github.com/internLM/tutorialInternLM&#xff1a;https://github.com/InternLM书生浦语官网&#xff1a;https://in…

c 编码(进行中)

编码出来的jpeg图片只有红&#xff0c;绿色。排查中 ​​​​​​​ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #in…

nestjs入门教程系列(三):动态路由到底该怎么用

如何获取前端请求中的参数 引子 我们在处理前端发送过来的请求时&#xff0c;需要访问客户端的request信息&#xff0c;那么我们该怎么操作呢?由于nestjs提供对底层平台的访问即express&#xff0c;所以我们可以通过注入的方式来方位请求对象 TIPS:需要从nestjs/common包里…

【C程序设计】C判断

判断结构要求程序员指定一个或多个要评估或测试的条件&#xff0c;以及条件为真时要执行的语句&#xff08;必需的&#xff09;和条件为假时要执行的语句&#xff08;可选的&#xff09;。 C 语言把任何非零和非空的值假定为 true&#xff0c;把零或 null 假定为 false。 下面…

自动化测试框架 —— pytest框架入门到入职篇

01、pytest框架介绍 pytest 是 python 的第三方单元测试框架&#xff0c;比自带 unittest 更简洁和高效&#xff0c;支持非常丰富的插件&#xff0c;同时兼容 unittest 框架。这就使得我们在 unittest 框架迁移到 pytest 框架的时候不需要重写代码。 pytest框架优点 1、简单…

网络故障排查和流量分析利器-Tcpdump命令

Tcpdump是一个在Unix/Linux系统上广泛使用的命令行网络抓包工具。它能够捕获经过网络接口的数据包&#xff0c;并将其以可读的格式输出到终端或文件中。Tcpdump是一个强大的命令行工具&#xff0c;能够捕获和分析网络数据包&#xff0c;为网络管理员和安全专业人员提供了深入了…

javascript中location对象的属性与方法

前言 本章介绍js中的location中的属性和方法。 文章目录 前言什么是location为什么要用locationlocation对象属性location对象方法总结 什么是location 在JavaScript中&#xff0c;location 是一个包含当前页面的URL信息的对象。它允许你获取和操作当前页面的URL&#xff0c;比…

java的mybatis

一.spring 整合单元测试 二.lombok 三.注解方式开发 四.xml 方式开发 五.动态sql

死锁-第三十四天

目录 什么是死锁 进程死锁、饥饿、死循环的区别 死锁产生的必要条件 什么时候会发生死锁 死锁的处理策略 本节思维导图 什么是死锁 每一个人都占有一个资源&#xff0c;同时又在等待另一个人手里的资源 进程死锁、饥饿、死循环的区别 死锁&#xff1a;各进程互相等待对…

虾皮跨境电商物流:为卖家提供高效灵活的物流解决方案

虾皮&#xff08;Shopee&#xff09;作为一家知名的跨境电商平台&#xff0c;其物流服务是其成功的关键因素之一。虾皮跨境电商物流服务为卖家提供了一站式的物流解决方案&#xff0c;从订单处理到最终交付&#xff0c;为卖家提供高效、灵活、成本效益高的物流服务&#xff0c;…

【c++】vector的特性及使用

目录 一、vector的介绍及使用 1、vector迭代器的使用 2、vector的空间增长 3、vector的迭代器失效问题 二、vector的深度剖析与模拟实现 一、vector的介绍及使用 1、vector迭代器的使用 vector的迭代器就是原生态指针。vector的迭代器使用方法与string的迭代器使用方法相…

springCloud之Stream

1、简介 Spring Cloud Stream是一个用来为微服务应用构建 消息驱动 能力的框架。通过使用 Spring Cloud Strea m &#xff0c;可以有效简化开发人员对消息中间件的使用复杂度&#xff0c;降低代码与消息中间件间的耦合度&#xff0c;屏蔽消息中间件 之 间的差异性&#xff0c;…

深入理解 BEM:前端开发中的命名约定革命

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

定时器PWM控制RGB彩灯案例

1.脉冲宽度调制PWM PWM&#xff08;Pulse Width Modulation&#xff09;简称脉宽调制&#xff0c;是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术&#xff0c;广泛应用在测量、通信、工控等方面。   PWM的一个优点是从处理器到​​ ​被控系统​​​信号…

【React】class组件生命周期函数的梳理和总结(第一篇)

1. 前言 本篇梳理和总结一下React的生命周期函数&#xff0c;方便使用class组件的同学查阅&#xff0c;先上生命周期图谱。 2. 生命周期函数 生命周期函数说明constructor(props) 功能&#xff1a;如果不需要初始化state或不进行方法绑定&#xff0c;class组件可以不用实现构造…

Cesium加载大规模三维数据渲染性能优化方案

根据实际项目经验和近期的论文&#xff0c;总结一下Cesium加载大规模三维数据性能优化方法。个人认为在实际的GIS数字孪生项目中,其可行的优化手段主要有三种&#xff1a; &#xff08;1&#xff09;通过专业的转换工具CesiumLab等对原始的三维模型进行轻量化处理&#xff0c;包…

【Linux Shell】2. Shell 变量

文章目录 【 1. 变量命名规则 】【 2. 变量的使用 】【 3. 只读变量 】【 4. 删除变量 】【 5. 变量类型 】【 6. Shell 字符串 】6.1 字符串的分类6.2 字符串操作 【 7. Shell 数组 】7.1 定义数组7.2 读取数组7.3 获取数组的长度 【 8. Shell 注释 】8.1 单行注释8.2 多行注释…