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

news/2024/5/17 17:47:18 标签: 并发编程同步锁, synchronized

synchronized_2">并发编程带来的安全性同步锁(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.niftyadmin.cn/n/5302507.html

相关文章

开源游戏引擎:创造无限可能 | 开源专题 No.56

godotengine/godot Stars: 62.6k License: MIT Godot Engine 是一个功能强大的跨平台游戏引擎&#xff0c;可用于创建 2D 和 3D 游戏。它提供了一套全面的常见工具&#xff0c;让用户可以专注于制作游戏而不必重复造轮子。该引擎支持将游戏一键导出到多个平台上&#xff0c;包…

Day22 二叉树part08 235.二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点

二叉树part08 235.二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点 235. 二叉搜索树的最近公共祖先 方法一&#xff1a;递归法&#xff08;利用二叉搜索树性质&#xff09; class Solution { public:TreeNode* lowestCommonAncestor(TreeN…

阿里云PolarDB数据库不同配置租用价格表

阿里云数据库PolarDB租用价格表&#xff0c;云数据库PolarDB MySQL版2核4GB&#xff08;通用&#xff09;、2个节点、60 GB存储空间55元5天&#xff0c;云数据库 PolarDB 分布式版标准版2核16G&#xff08;通用&#xff09;57.6元3天&#xff0c;阿里云百科aliyunbaike.com分享…

机器视觉系统选型-选型-总结

一&#xff1a;明确需求 需求&#xff1a;镜面材质上的划痕检测&#xff0c;传送线上运动过程中拍照&#xff0c;无景深要求&#xff0c;传送线速度0.8m/s&#xff0c;产品间隔50mm 产品大小&#xff1a;100*80mm 工作距离限制&#xff1a;≤ 300mm 最小划痕宽度&#xff1a;0.…

【node】Error message “error:0308010C:digital envelope routines::unsupported“

Error: error:0308010C:digital envelope routines::unsupported 版本 【nvm】node版本升降版本 错误 LuckyLucky MINGW64 /e/_DockerDesktop/strong/strong-data (master) $ npm run serve> smallwei/strong-data2.0.1 serve > vue-cli-service serveBrowserslist: …

【开题报告】基于SSM的农业科技宣传系统的设计与实现

1.选题背景 随着科技的发展和农业生产模式的转型&#xff0c;农业科技在提高农业生产效率、优化资源利用、保护环境等方面起到了重要作用。然而&#xff0c;由于信息不对称和传播渠道有限&#xff0c;许多农民和相关人员无法及时获取到最新的农业科技成果和技术指导&#xff0…

程序媛的mac修炼手册--MacOS系统更新升级史

啊&#xff0c;我这个口罩三年从未感染过新冠的天选免疫王&#xff0c;却被支原体击倒&#x1f637;大意了&#xff0c;前几天去医院体检&#xff0c;刚检查完出医院就摘口罩了&#x1f926;大伙儿还是要注意戴口罩&#xff0c;保重身体啊&#xff01;身体欠恙&#xff0c;就闲…

59.网游逆向分析与插件开发-游戏增加自动化助手接口-文字资源读取类的C++还原

内容来源于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;游戏菜单文字资源读取的逆向分析-CSDN博客 之前的内容&#xff1a;接管游戏的自动药水设定功能-CSDN博客 码云地址&#xff08;master分支&#xff09;&#xff1a;https://gitee.com/dye_your_fing…