synchronized 关键字

synchronized是Java中的关键字,它用于实现同步访问共享资源,可解决共享资源竞争问题,以确保多个线程之间共享资源访问的正确性。当一个方法或代码块被声明为synchronized时,只有一个线程可以执行该方法或代码块。其他尝试访问该方法或代码块的线程将会被阻塞,直到当前线程执行完该方法或代码块。需要说明的是,synchronized关键字可能会导致线程阻塞和性能下降。因此,在使用synchronized关键字时,应该仔细考虑同步访问的必要性,并尽可能地减小同步访问的范围。

synchronized 与 Java 内存模型

synchronized关键字可保证所修饰元素的原子性、可见性、有序性。
对于非原子性操作,诸如i++那样的操作,可以借助synchronized来保证整个代码块的原子性
synchronized也可实现可见性。synchronized同步块的可见性是由“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中”这个规则保证。
对于有序性,syncronizd关键字通过“一个变量在同一时刻只允许一个线程对其进行lock操作”这个规则来保证。

synchronized 使用场景

根据synchronized关键字修饰元素的类型,可将其使用场景分为两类:使用syncronized修饰代码块(也称同步代码块)或使用syncronized修饰方法(也称为同步方法)。其中,使用syncronized修饰方法,又根据是否是static方法,分为使用syncronized修饰类方法和syncronized修饰对象方法。

synchronized作用于代码块

synchronized关键字作用于方法中的代码块,此时基于 synchronized 指定实例加锁。用法实例如下:

synchronized(instanceName) { // instanceName 表示指定实例/* 代码块 */
}

synchronized作用于对象方法

synchronized关键字作用于对象方法,此时基于当前对象加锁。使用模式如下:

synchronized 访问类型 返回值 methodName() {/* 方法体 */
}

synchronized作用于类方法

synchronized关键字作用于对象方法,此时基于当前类加锁。使用模式如下:

synchronized 访问类型 static 返回值 methodName() {/* 方法体 */
}

使用syncronized关键字可以保证在同一时刻仅有一个线程访问该同步代码块或同步方法。synchronized 通过下述规则保证该特性:
(1) 当多个线程同时访问synchronized同步块或同步方法时,只能有一个线程得到执行,其他线程必须等待。
(2) 当一个线程访问synchronized同步块或同步方法时,其他线程仍可同时调用object中其它非synchronized同步块或方法。
(3) 当一个线程访问synchronized同步块或同步方法时,它会获得整个object的对象锁或类锁。因此,其他线程对于object的所有同步块或同步方法的访问都将被暂时阻塞,直至当前线程执行完这个同步块或同步方法。
(4) 当线程完成synchronized同步块或同步方法访问时,它会释放整个object的对象锁或类锁。因此,需要调用object上同步块或同步方法的其他线程将尝试获取这个object的对象锁。

synchronized 原理

synchronized 本质使用 Java 内置的 Monitor(管程、监视器)Object 实现线程同步。对同步代码块和同步方法,使用不同的实现方式。对于同步代码块,则是使用 monitorenter 和 monitorexit 指令 实现的;而同步方法,则是采用 ACC_SYNCHRONIZED标记符实现。
(1) 同步代码块
monitorenter 指令插入到同步代码块的开始位置,monitorexit 指令插入到同步代码块的结束位置,JVM则保证每一个monitorenter都有一个monitorexit与之相对应。任何对象都有一个monitor与之相关联,当且一个monitor被持有之后,该对象将处于锁定状态。
根据虚拟机规范的要求,在执行monitorenter指令时,首先要尝试获取对象的锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象的锁,把锁的计数器加1,相应的,在执行monitorexit指令时会将锁计数器减1,当计数器为0时,锁就被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。
注意:(a)首先,synchronized同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题;(b)其次,同步块在已进入的线程执行完之前,会阻塞后面其他线程的进入。
(2) 同步方法
当某个线程要访问该方法时,会检查该方法是否有 ACC_SYNCHRONIZED 标志,如果有设置,则需要先获得监视器锁,然后开始执行方法,方法执行之后再释放监视器锁。

Java 实现 Monitor Object简介

不同虚拟机,对Monitor Object的实现略有不同。这里以HotSpot为例,简单介绍monitor的实现(基于ObjectMonitor实现)。ObjectMonitor的主要数据结构如下:

ObjectMonitor() {_header       = NULL;_count        = 0; // 记录个数_waiters      = 0,_recursions   = 0;_object       = NULL;_owner        = NULL; // ObjectMonitor的持有者_WaitSet      = NULL; // 处于wait状态的线程,会被加入到_WaitSet_WaitSetLock  = 0 ;_Responsible  = NULL ;_succ         = NULL ;_cxq          = NULL ;FreeNext      = NULL ;_EntryList    = NULL ; // 处于等待锁block状态的线程,会被加入到该列表_SpinFreq     = 0 ;_SpinClock    = 0 ;OwnerIsThread = 0 ;}

可以看到,ObjectMonitor中有两个队列,_WaitSet 和_EntryList,用来保存ObjectWaiter对象列表(等待锁的线程都会被封装成ObjectWaiter对象),_owner指向持有ObjectMonitor对象的线程。当多个线程同时访问一段同步代码时,首先会进入_EntryList 集合,当线程获取到对象的 monitor 后进入 _Owner 区域并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1;若线程调用 wait() 方法,将释放当前持有的monitor,owner变量恢复为null,count自减1,同时该线程进入 WaitSet集合中等待被唤醒。若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。
请添加图片描述

参考

《Java编程思想》(第四版) Bruce Eckel [译]陈昊鹏
《java并发编程实战》 Brian Goetz 等著 童云兰 等译
https://www.baidu.com/ 百度AI搜索
https://blog.csdn.net/chenssy/article/details/54883355 【死磕Java并发】-----深入分析synchronized的实现原理
https://www.hollischuang.com/archives/1883 深入理解多线程(一)——Synchronized的实现原理
https://blog.csdn.net/javazejian/article/details/72828483 深入理解Java并发之synchronized实现原理
http://www.cnblogs.com/paddix/p/5367116.html Java并发编程:Synchronized及其实现原理
https://blog.csdn.net/qq_42080839/article/details/109848719 Java并发深度总结:synchronized 关键字

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试题 17.16. 按摩师

按摩师(easy) 题目链接: 面试题 17.16. 按摩师 题目描述: 一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列&#xff0c…

冠达管理:股票退市整理期?

近些年来,随着我国股市的发展,股票市场的出资者逐渐增多。但在出资过程中,退市股票的问题也成为了备受重视的论题。那么,股票退市收拾期到底是什么?如何应对退市股票? 首要,什么是股票退市收拾…

【ES】笔记-Class类剖析

Class Class介绍与初体验ES5 通过构造函数实例化对象ES6 通过Class中的constructor实列化对象 Class 静态成员实例对象与函数对象的属性不相通实例对象与函数对象原型上的属性是相通的Class中对于static 标注的对象和方法不属于实列对象,属于类。 ES5构造函数继承Cl…

【LeetCode-中等题】46. 全排列

文章目录 组合并集问题汇总:题目方法一:递归回溯 组合并集问题汇总: 1、子集去重版本 2、组合去重版本 3、子集非去重版本 题目 这题中nums中的数各不相同,所以不需要去重: 而下面这题,nums中的数会存在重…

javaee spring 测试aop 切面

切面类 package com.test.advice;import org.aspectj.lang.ProceedingJoinPoint;//增强类 public class MyAdvice {//将这个增强方法切入到service层的add方法前public void before(){System.out.println("添加用户之前");}}目标类 package com.test.service;publi…

【脑机接口】基于运动想象的康复指导在脑卒中偏瘫患者中的应用

【摘要】 目的 探讨运动想象康复指导对脑卒中偏瘫患者的康复效果及意义。 方法 将 60例脑卒中偏瘫患者随机分为观察组(n31)和对照组(n29),对照组的康复训练指导采用讲解示范法,观察组采用运动想象法 。比较两组 患者 的运 动功能 、日常生活 活动能力及 …

【EI会议征稿】第三届机械自动化与电子信息工程国际学术会议(MAEIE 2023)

第三届机械自动化与电子信息工程国际学术会议(MAEIE 2023) 第三届机械自动化与电子信息工程国际学术会议(MAEIE 2023)将于2023年12月15-17日在江苏南京举行。本会议通过与业内众多平台、社会各团体协力,聚集机械自动…

RabbitMQ学习笔记

1、什么是MQ? MQ全称message queue(消息队列),本质是一个队列,FIFO先进先出,是消息传送过程中保存消息的容器,多 用于分布式系统之间进行通信。 在互联网架构中,MQ是一种非常常见的…

【数据库】MySQL基础知识全解

系列综述: 💞目的:本系列是个人整理为了秋招面试的,整理期间苛求每个知识点,平衡理解简易度与深入程度。 🥰来源:材料主要源于拓跋阿秀、小林coding等大佬博客进行的,每个知识点的修…

一百七十三、Flume——Flume写入HDFS后的诸多小文件问题

一、目的 在用Flume采集Kafka中的数据写入HDFS后,发现写入HDFS的不是每天一个文件,而是一个文件夹,里面有很多小文件,浪费namenode的宝贵资源 二、Flume的配置文件优化(参考了其他博文) (一&a…

c语言初阶指针

目录 何为指针 地址大小 野指针 成因 如何规避 有效性 指针计算 -整数 ​编辑 指针比较运算 指针-指针 ​编辑 数组与指针关系 二级指针 指针数组 应用 何为指针 指针就是指针变量,用来存放内存空间的一个编号,将指针比作我们宾馆的客人&a…

java基础(三)

101.如何让写出去的数据能成功生效? flush()刷新数据 close()方法是关闭流,关闭包含刷新,关闭后流不可以继续使用了。 102.学会字节流完成文件的复制(支持一切的文件) public class CopyDemo05 { public static void main(St…