【JavaEE】Java中复杂的Synchronized关键字

news2024/11/29 0:30:10

目录

 一、synchronized的特性

(1)互斥

(2)刷新内存

(3)可重入

二、synchronized的使用

(1)修饰普通方法

(2)修饰静态方法

(3)修饰代码块

三、synchronized的锁机制

(1)基本特点

(2)加锁工作过程

1.偏向锁

2.轻量级锁

3.重量级锁

(3)优化操作

1.锁消除

2.锁粗化

四、synchronized和volatile的区别


 一、synchronized的特性

(1)互斥

         synchronized 通过互斥达到原子性(线程安全的四大特性之一)

        synchronized 会起到互斥效果,某个线程执行到某个对象的 synchronized 中时,其他线程如果也执行到同一个对象 synchronized 就会阻塞等待。同一时间,只能一个线程拥有这把锁,去执行代码。
1.进入 synchronized 修饰的代码块, 相当于 加锁。
2.退出 synchronized 修饰的代码块, 相当于 解锁。

synchronized void increase(){//进入方法内部,相当于针对当前对象 
        count++;

}//执行完毕相当于针对当前对象“解锁”

        synchronized用的锁是存在Java对象头里的。synchronized的底层是使用操作系统的mutex lock实现的。
        可以粗略理解成, 每个对象在内存中存储的时候, 都存有一块内存表示当前的 "锁定" 状态。如果当前是 "未锁定" 状态, 那么就可以使用, 使用时需要设为 "锁定" 状态。如果当前是 "锁定" 状态, 那么其他人无法使用, 只能排队等待。一个线程先上了锁,其他线程只能等待这个线程释放。

        注意点:

        针对每一把锁,操作系统内部都维护了一个等待队列。当这个锁被某个线程占有的时候, 其他线程尝试进行加锁,就加不上了, 就会阻塞等待, 一直等到之前的线程解锁之后,由操作系统唤醒一个新的线程,再来获取到这个锁。

        上一个线程解锁之后, 下一个线程并不是立即就能获取到锁。而是要靠操作系统来 "唤醒"。 这也就是操作系统线程调度的一部分工作。
        假设有 A B C 三个线程,线程 A 先获取到锁,然后 B 尝试获取锁, 然后 C 再尝试获取锁,此时 B和 C 都在阻塞队列中排队等待。 但是当 A 释放锁之后, 虽然 B 比 C 先来的, 但是 B 不一定就能获取到锁, 而是和 C 重新竞争,并不遵守先来后到的规则

(2)刷新内存

        synchronized 通过加锁减锁能保证内存可见性。

synchronized 的工作过程:
1. 获得互斥锁
2. 从主内存拷贝变量的最新副本到工作的内存
3. 执行代码
4. 将更改后的共享变量的值刷新到主内存
5. 释放互斥锁
所以 synchronized 也能保证内存可见性。

(3)可重入

         synchronized 只能有一定约束,并不能完全禁止指令重排序。synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题。

// 第一次加锁, 加锁成功
lock();
// 第二次加锁, 锁已经被占用, 阻塞等待.
lock();

        对于把自己锁死,就是一个线程没有释放锁,然后又尝试再次加锁。

        按照之前对于锁的设定, 第二次加锁的时候会阻塞等待。直到第一次的锁被释放, 才能获取到第二个锁。释放第一个锁也是由该线程来完成, 结果这个线程已经阻塞等待了,也就无法进行解锁操作。这时候就会死锁。这样的锁称为不可重入锁。

        Java中的synchronized是可重入锁,因此不会出现上述问题。可重入锁的内部,包含了 "线程持有者" 和 "计数器" 两个信息。如果某个线程进行加锁的时候, 发现锁已经被人占用, 占用者恰好是自己,那么仍然可以继续获取到锁, 并让计数器自增。解锁的时候就是当计数器递减为0的时候, 才真正释放锁,这时候锁才能被别的线程获取到。

二、synchronized的使用

(1)修饰普通方法

锁的 SynchronizedDemo 对象

public class SynchronizedDemo {
    public synchronized void methond() {
    }
}

(2)修饰静态方法

锁的 SynchronizedDemo 类的对象
 

public class SynchronizedDemo {
    public synchronized static void method() {
    }
}

(3)修饰代码块

明确指定锁的对象

锁当前对象

public class SynchronizedDemo {
    public void method() {
    synchronized (this) {
        }
    }
}

锁类对象

public class SynchronizedDemo {
    public void method() {
        synchronized (SynchronizedDemo.class) {
        }
    }
}

        需要注意的是两个线程竞争同一把锁,才会产生阻塞等待。两个线程分别尝试获取两把不同的锁,不会产生竞争。

三、synchronized的锁机制

(1)基本特点

        只考虑 JDK 1.8,加锁工作过程:JVM 将 synchronized 锁分为 无锁、偏向锁、轻量级锁、重量级锁 状态。会根据情况,进行依次升级。
1. 开始时是乐观锁, 如果锁冲突频繁, 就转换为悲观锁.
2. 开始是轻量级锁实现, 如果锁被持有的时间较长, 就转换成重量级锁.
3. 实现轻量级锁的时候大概率用到的自旋锁策略
4. 是一种不公平锁
5. 是一种可重入锁
6. 不是读写锁

(2)加锁工作过程

1.偏向锁

        第一个尝试加锁的线程,优先进入偏向锁状态。
        偏向锁不是真的 "加锁", 只是给对象头中做一个 "偏向锁的标记",记录这个锁属于哪个线程。如果后续没有其他线程来竞争该锁,那么就不用进行其他同步操作了,避免了加锁解锁的开销。如果后续有其他线程来竞争该锁,因为刚才已经在锁对象中记录了当前锁属于哪个线程了, 很容易识别当前申请锁的线程是不是之前记录的线程, 那就取消原来的偏向锁状态, 进入一般的轻量级锁状态。偏向锁本质上相当于 "延迟加锁"。能不加锁就不加锁,尽量来避免不必要的加锁开销。但是该做的标记还是得做的, 否则无法区分何时需要真正加锁。

        面试中会经常问到什么是偏向锁。偏向锁不是真的加锁,而只是在锁的对象头中记录一个标记,记录该锁所属的线程。如果没有其他线程参与竞争锁,那么就不会真正执行加锁操作,从而降低程序开销。一旦真的涉及到其他的线程竞争,再取消偏向锁状态,进入轻量级锁状态。

2.轻量级锁

        随着其他线程进入竞争,偏向锁状态被消除, 进入轻量级锁状态(自适应的自旋锁)。
此处的轻量级锁就是通过 CAS 来实现。
        通过 CAS 检查并更新一块内存 (比如 null => 该线程引用)。如果更新成功,则认为加锁成功。如果更新失败, 则认为锁被占用, 继续自旋式的等待(并不放弃 CPU)。
自旋操作是一直让 CPU 空转, 比较浪费 CPU 资源。因此此处的自旋不会一直持续进行, 而是达到一定的时间、重试次数, 就不再自旋了。也就是所谓的 "自适应"

3.重量级锁

        如果竞争进一步激烈, 自旋不能快速获取到锁状态,就会膨胀为重量级锁。此处的重量级锁就是指用到内核提供的 mutex。
        执行加锁操作, 先进入内核态。在内核态判定当前锁是否已经被占用。如果该锁没有占用, 则加锁成功, 并切换回用户态。如果该锁被占用,则加锁失败。 此时线程进入锁的等待队列、挂起。 等待被操作系统唤醒。经历了一系列的操作, 这个锁被其他线程释放了, 操作系统也想起了这个挂起的线程,于是唤醒。这个线程, 尝试重新获取锁。

(3)优化操作

1.锁消除

编译器+JVM 判断锁是否可消除。 如果可以, 就直接消除。

锁消除:有些应用程序的代码中, 用到了 synchronized, 但其实没有在多线程环境下。 (例如StringBuffer)此时每个 append 的调用都会涉及加锁、解锁。但如果只是在单线程中执行这个代码, 那么这些加锁解锁操作是没有必要的,,白白浪费了一些资源开销。可以进行消除操作。
StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
sb.append("c");
sb.append("d");

2.锁粗化

一段逻辑中如果出现多次加锁解锁,编译器 + JVM 会自动进行锁的粗化。

锁的粒度: 粗和细

        实际开发过程中,使用细粒度锁,是期望释放锁的时候其他线程能使用锁。但是实际上可能并没有其他线程来抢占这个锁。这种情况 JVM 就会自动把锁粗化, 避免频繁申请释放锁。例如给下属交代工作任务:方式一:打电话,交代任务1, 挂电话。打电话,交代任务2,挂电话。打电话, 交代任务3, 挂电话。方式二:打电话,交代任务1,任务2,任务3,挂电话。显然,方式二是更高效的方案。这就是锁粗化的一个过程。

四、synchronized和volatile的区别

        synchronized和volatile都是Java关键字,并且都是解决线程安全的

 的方式,所以在面试的时候经常会被放到一起问。

        两者其实并没有联系。

        synchronized:

        1.通过加锁、解锁的方式,把一堆代码绑在一起,来保证原子性。

        2.通过加锁、解锁的方式, 来保证内存可见性。

        3.对指令重排序有一定约束。

        volatile:

        1.不能保证原子性。

        2.保证内存可见性。

        3.禁止指令重排序。       

       虽然synchronized在大多数情况下,都可以保证线程安全的。但是也不能在任何情况下都用synchronized的。synchronized是要付出一定代价的。synchronized是通过加锁、解锁的方式来保证的。所以,其他线程抢不到锁的时候,线程就会阻塞。线程就会放弃CPU,放弃之后,被重新调用的时间是不确定的。当使用synchronized就一定程度上放弃了高性能。使用volatile不会造成线程阻塞,但对性能也有一定影响,不过没有synchronized影响大。

        使用多线程是为了提高效率。使用synchronized,就代表放弃了一定效率。这两者需要平衡。      


 

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

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

相关文章

计网之初识网络(理解网络传输的基本流程)

文章目录一. 网络发展史二. IP地址和端口号三. 计算机网络分层1. 什么是网络协议2. OSI七层网络模型3. TCP/IP五层网络模型4. 数据在各个层的传输过程5. 网络设备所在分层一. 网络发展史 🍂独立模式(单机模式) 我们最初的计算机是在单机模式下使用的, 此时的计算机…

遇到“独自开”,开发出属于自己一套专属系统的时代还会远吗?

目录 一、前言 二、介绍 三、详细介绍 四、总结 一、前言 哈喽,大家好,我是追,看到标题独自开时,可能此处会有疑问了。独自开?半山居士王安石的“墙角数枝梅,凌寒独自开”?哈哈,…

北京化工大学2/7寒假集训题解(>1800)

目录 A-Fence B-D again​ C-Cut the Sequence D-Parade E-trade A-Fence #include<algorithm> #include<string.h> #include<stdio.h> #include<queue> using namespace std; struct nob {int v,p;bool operator <(const nob &a)const{retu…

网络协议(四):网络分类、ISP、上网方式、公网私网、NAT

网络协议系列文章 网络协议(一)&#xff1a;基本概念、计算机之间的连接方式 网络协议(二)&#xff1a;MAC地址、IP地址、子网掩码、子网和超网 网络协议(三)&#xff1a;路由器原理及数据包传输过程 网络协议(四)&#xff1a;网络分类、ISP、上网方式、公网私网、NAT 目录…

【沁恒WCH CH32V307V-R1开发板两路ADC读取实验】

【沁恒WCH CH32V307V-R1开发板两路ADC读取实验】1. 前言2. 软件配置2.1 安装MounRiver Studio3. ADC项目测试3.1 打开ADC工程3.2 编译项目4. 下载验证4.1 接线4.2 演示效果5. 小结1. 前言 ADC 模块包含 2 个 12 位的逐次逼近型的模拟数字转换器&#xff0c;最高 14MHz 的输入时…

2022年这5款熟悉的软件退出了历史舞台

在过去的一年里&#xff0c;有很多新产品发布&#xff0c;当然也有很多产品与我们就此别过。这些产品曾陪伴我们的生活&#xff0c;给我们带来欢乐&#xff0c;帮助我们成长。所以本文将盘点一下在2022年和我们告别的产品。1.微软IE浏览器IE浏览器1995年8月16日正式上线&#x…

window 安装debian的Linux系统+一些环境初始化

文章目录一、安装&#xff1a;1、安装WSL22、微软商店搜索debian安装&#xff1a;3、也可以官方安装&#xff1a;二、更改镜像源1、查看debian系统版本&#xff1a;2、修改3、升级三、安装zsh1&#xff1a;检查2、安装zsh3、安装oh-my-zsh4、安装插件5、配置文件~/.zshrc:6、配…

软件工程详细知识点(下)

文章目录七、面向对象的分析设计1、面向对象分析&#xff08;OOA&#xff09;2、面向对象设计&#xff08;OOD&#xff09;八、编码1、程序设计语言九、软件测试十、软件维护十一、软件项目管理RUP&#xff08;统一软件开发过程&#xff09;面向对象编程和面向对象设计的五个基…

【C++STL】双向循环链表与其迭代器的深度剖析及实现(百字短文速通)

1&#xff0c;双向循环链表基本结构的实现&#xff08;不包含需要迭代器的部分&#xff09;先用struct封装链表的节点&#xff0c;这里我们仅需要提供一个构造函数即可&#xff0c;并且构造函数必须提供缺省值&#xff0c;因为会有如下使用场景&#xff1a;new Node();此时需要…

crawler爬虫抓取数据

crawler爬虫实现 学习目标&#xff1a; 了解 crawler爬虫运行流程了解 crawler爬虫模块实现 1. crawler功能 初始化driver输入公司名称,并点击判断是否需要验证如果需要验证&#xff0c;获取验证图片并保存获取打码坐标点击验证图片判断查询结果选择第一条查询结果获取主要信…

电脑自带的录屏软件在哪?图文教学,教你如何快速录屏

很多小伙伴或许都听说过电脑有一款自带的录屏软件&#xff0c;但却不知道这款录屏软件在哪里。电脑自带的录屏软件在哪&#xff1f;其实很简单&#xff0c;如果你的电脑是Win10或者Win11的电脑&#xff0c;那么就可以使用电脑自带的录屏软件&#xff0c;一起跟着小编来看看吧。…

初次认识C++类

目录 前言&#xff1a; 面向过程和面向对象的区别&#xff1a; C语言&#xff1a; C&#xff1a; 类的引入&#xff1a; 类的定义&#xff1a; 类的权限&#xff1a; 类的作用域&#xff1a; 类的实例化&#xff1a; 类的大小计算&#xff1a; 空类或则只…

急速肝了一波ChatGPT,听说阿里面试题都没问题~

目录前言注册步骤&#xff1a;最后总结前言 互联网圈子里面ChatGPT现在实在是太火了&#xff0c;但是你还没用过&#xff1f;我只能说你OUT了&#xff0c;ChatGPT是什么呢&#xff1f; 由人工智能实验室OpenAI发布的对话式大型语言模型ChatGPT引爆中文互联网。它可以与人类轻松…

2022年ts学习记录

以下记录的是&#xff0c;我在学习中的一些学习笔记&#xff0c;这篇笔记是自己学习的学习大杂烩&#xff0c;主要用于记录&#xff0c;方便查找一、TS 是什么 &#xff1f;##1、简介TS&#xff1a;是TypeScript的简称&#xff0c;是一种由微软开发的自由和开源的编程语言。ts …

朗润外盘国际期货:SC原油市场情绪回暖领涨期市

今日值得回溯的三个行情&#xff1a;①SC原油主力合约今日收高4.23%&#xff0c;研报建议仍以震荡行情对待&#xff1f;②沪锡主力合约收涨3.20%&#xff0c;现在做多合适吗&#xff1f;③鸡蛋主力合约收跌1.32%&#xff0c;研报称这只是小幅回调。 【今日期市盘面概况】 整体…

深度学习 Day25——使用Pytorch实现彩色图片识别

深度学习 Day25——使用Pytorch实现彩色图片识别 文章目录深度学习 Day25——使用Pytorch实现彩色图片识别一、前言二、我的环境三、前期工作1、导入依赖项和设置GPU2、下载数据3、加载数据4、数据可视化四、构建CNN网络结构1、函数介绍2、构建CNN并打印模型3、可视化模型结构五…

Docker安装EalasticSearch、Kibana,安装Elasticvue插件

使用Docker快速安装部署ES和Kibana的前提&#xff1a;首先需要确保已经安装了Docker环境。 如果没有安装Docker的话&#xff0c;先在Linux上安装Docker。 有了Docker环境后&#xff0c;就可以使用Docker安装部署ES和Kibana了 一、安装ES 1、拉取EalasticSearch镜像 docker p…

SpringIOC之创建Bean的核心方法doGetBean

概述面向资源&#xff08;XML、Properties&#xff09;、面向注解定义的 Bean 是如何被解析成 BeanDefinition&#xff08;Bean 的“前身”&#xff09;&#xff0c;并保存至 BeanDefinitionRegistry 注册中心里面&#xff0c;实际也是通过 ConcurrentHashMap 进行保存。Spring…

强缓存与协商缓存

Ⅰ、http缓存 HTTP 缓存策略 分为 > 「强制缓存」 和 「协商缓存」 为什么需要 HTTP 缓存 呢 ? &#x1f447; 直接使用缓存速度 >> 远比重新请求快 缓存对象有那些呢 &#xff1f;&#x1f447; 「图片」 「JS文件」 「CSS文件」 等等 文章目录Ⅰ、http缓存Ⅱ…

Hi3559av100平台8路1080P/720P输入配置流程梳理

平台&#xff1a; hi3559av100 硬件连接&#xff1a; 8路YUV422 -> 4路 2lane MIPI -> hi3559av100 最终效果&#xff1a; 经过处理后&#xff0c;后端可以实现8路独立视频流处理&#xff1b; 可以自由和后级VPSS/VENC/VO等模块进行绑定和处理 前言&#xff1a; &…