java 同步锁_Java多线程:synchronized同步锁的使用和实现原理

作用和用法

  • 在多线程对共享资源进行并发访问方面,JDK提供了synchronized关键字来进行线程同步,实现多线程并发访问的线程安全。synchronized的作用主要体现在三个方面:(1)确保线程互斥地访问同步代码;(2)保证共享变量的线程可见性;(3)禁止指令重排。其中(2)和(3)相当于volatile关键字的作用。
  • synchronized可以用在代码的以下地方:

(1)静态方法:将类对象自身作为monitor对象,对该类所有使用了sychronized修饰的静态方法进行同步,即任何时候只能存在一个线程在调用该类的使用了synchronized修饰的静态方法,其他调用了该类的使用了synchronized修饰的静态方法的线程需要阻塞;

(2)普通成员方法:使用类的对象实例作为monitor对象,该类所有使用了synchronized修饰的成员方法,在任何时刻只能被一个线程访问,其他线程需要阻塞;

(3)代码块:使用某个对象作为monitor对象,通常为一个普通的private成员变量,如private Object object = new Object();,这样所有使用了该object对象的同步块,在任何时候只能存在一个线程访问。

  • synchronized可以与monitor对象的wait,notify,notifyAll方法一起来使用,实现线程之间的通信,如实现生产者和消费者模型。其中多个线程共享一个monitor对象,在线程持有synchronized锁时,才能调用monitor的wait,notify或者notifyAll,分别用于释放monitor锁,阻塞休眠,等待其他线程;通知和唤醒其中一个阻塞休眠的线程,让该线程去获取monitor锁;通知所有阻塞休眠的线程去竞争monitor锁。
  • synchronized使用方便,无需显示地在应用代码中加锁和解锁,只需在对应的方法或者代码块中使用synchronized关键字修饰即可,由JVM自身实现自动地加锁和释放锁。
  • synchronized修饰的范围越小,线程并发度越高,性能越好,所以通常使用同步代码块,而不是同步方法来缩小同步范围,优化性能。

实现原理

JVM层面

  • synchronized关键字是基于JVM提供的monitorenter和monitorexit字节码指令,以及结合监视器monitor来实现的。
  • 由上面的分析可知,synchronized关键字用在静态方法,普通成员方法,代码块中,分别需要以类对象自身,类的对象实例,某个普通对象作为对应的monitor对象。
  • 由JVM的相关知识可知,任何java类都需要编译成class字节码,然后加载到JVM当中去执行。而在编译一个java类生成对应class字节码时,当遇到sychronized关键字时,会在sychronized关键字所修饰的方法或者代码块的开始处:增加一个monitorenter字节码指令,在方法或者代码块的结束处:增加monitorexit字节码指令,即使用monitorenter和monitorexit字节码指令包围该方法或者代码块对应的字节码。如下:
  • 在类的成员方法中使用synchronized关键字:
7dbbd2b9d8461ac562cb66f83c3c2130.png
  • 反编译该类对应的class字节码文件:在成员方法method对应的字节码周围使用了monitorenter和monitorexit字节码指令。
66ee98bf3cceb18fb1c6c74a9d8816de.png

monitorenter和monitorexit指令的作用为:

  • monitorenter的作用:所有线程共享该同步代码和该对象关联的监视器monitor,每个线程执行到monitorenter指令的时候,会检查对应的monitor对象的计数是否为0,是则当前线程成为该monitor对象的owner,即锁住该monitor对象了,并递增该计数为1,之后该线程每调用一次使用了该monitor对象进行同步的方法,计数加一(所以synchronized也是可重入的);其他线程检查到monitor对象的计数不为0,则知道该monitor对象已经被其他线程持有锁住了,故当前线程会阻塞直到该monitor的计数重新变为0,则阻塞的线程们会继续竞争成为该monitor的owner,从而可以访问同步代码。
  • monitorexit的作用:当持有该monitor对象的线程每执行完一个同步代码时(如对于成员方法,如果该线程调用了多个使用sychronized修饰的成员方法,则每个方法执行完执行一次monitor减一),将monitor的计数减一,当monitor对象的计数递减到0时,则当前线程不再持有该monitor对象,其他阻塞的线程此时可以竞争成为该monitor的owner,成功的线程可以访问同步代码。
  • 为什么monitor对象的wait,notify,notifyAll需要在synchronized同步代码里面使用呢?首先需要理解以下概念:
  1. 每个对象关联一个监视器monitor;
  2. 每个监视器monitor都有一个该对象的锁(即计数是否为0,为0则说明没有其他线程加锁),一个等待队列和一个同步队列;
  • wait方法:释放对象锁,然后进入等待队列;
  • notify和notifyAll方法:从等待队列被唤醒,放到同步队列去竞争该对象锁;
  • 所以线程在执行wait,notify,notifyAll时需要依赖该监视器monitor,即该线程成为该监视器的owner,从而可以访问synchronized包围的同步代码,这样才能有权访问该监视器对应的对象锁,等待队列和同步队列。

操作系统层面

  • 在操作系统层面,synchronized是基于操作系统的Metux Lock来实现的,而操作系统实现线程之间的切换是需要进行上下文切换的,即从用户态切换到内核态,所以这也是synchronized相对来说成本较高,性能相对较低的原因。

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

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

相关文章

Apache shutdown unexpectedly启动错误解决方法

xampp启动时显示的错误为: 9:52:41 [Apache] Attempting to start Apache app... 9:52:41 [Apache] Status change detected: running 9:52:42 [Apache] Status change detected: stopped 9:52:42 [Apache] Error: Apache shutdown unexpectedly. 9:52:42 …

java基础代码实例_基础篇:详解JAVA对象实例化过程

1 对象的实例化过程对象的实例化过程是分成两部分:类的加载初始化,对象的初始化要创建类的对象实例需要先加载并初始化该类,main方法所在的类需要先加载和初始化类初始化就是执行方法,对象实例化是执行方法一个子类要初始化需要先…

搭建webUI自动化及问题解决:Message: ‘chromedriver‘ executable needs to be in PATH.解决办法

搭建webUI自动化环境 1、conda install selenium即可。 若出现:Message: chromedriver executable needs to be in PATH.Please see https://sites.google.com/a/chromium.org/chromedriver/home。 报错原因:没有配置chrome浏览器的chromedriver 解决…

python内置输入函数_python内置函数 print()

英文文档:print(*objects, sep’ ‘, end’\n’, filesys.stdout, flushFalse)Print objects to the text stream file, separated by sep and followed by end. sep, end, file and flush, if present, must be given as keyword arguments.All non-keyword argume…

webUI自动化一元素定位

实现功能:自动在百度输入框输入test,并进行搜索 from selenium import webdriver from time import sleepdriver webdriver.Chrome() driver.get("http://www.baidu.com")input_text driver.find_element_by_id("kw") print(&quo…

python propresql mysql_python数据库操作mysql:pymysql、sqlalchemy常见用法详解

本文实例讲述了python数据库操作mysql:pymysql、sqlalchemy常见用法。分享给大家供大家参考,具体如下:相关内容:使用pymysql直接操作mysql创建表查看表修改表删除表插入数据查看数据修改数据删除数据使用sqlmary操作mysql创建表查…

webUI自动化二-获取元素信息相关方法

from selenium import webdriver from time import sleepdriver webdriver.Chrome() driver.get("D:\QQFile\注册A.html")# 1获取用户输入框的大小 input_text driver.find_element_by_id("userA") print("元素大小为:",input_text…

python环境变量的配置 alias_配置别名

有没有经常敲错命令?比如git status?status这个单词真心不好记。如果敲git st就表示git status那就简单多了,当然这种偷懒的办法我们是极力赞成的。我们只需要敲一行命令,告诉Git,以后st就表示status:$ git…

C语言-字符串处理函数strcpy

strcpy 原型:strcpy(char destination[], const char source[]); 功能:将字符串source拷贝到字符串destination中。此处将source中的字符串结束标志符‘\0’也一同复制。所以在输出时,切不可以用‘\0’,puts,printf输…

pythonjavascript一起开发_Python开发【第十一篇】:JavaScript

JavaScript是一门编程语言,浏览器内置了JavaScript语言的解释器,所以在浏览器上按照JavaScript语言的规则编写相应代码之,浏览器可以解释并做出相应的处理。一、如何编写1、JavaScript代码存在形式Js代码内容2、JavaScript代码存放位置HTML的…

C语言-字符串处理函数strcat

strccat-字符串拼接函数 char*strcat(char* strDestination, const char* strSource); 参数说明: strDestination:目的字符串;strSource:源字符串。 strcat() 函数把 strSource 所指向的字符串追加到 strDestination 所指向的字…

js数组截取前5个_我不能没有的5个Vue.js库

1.Click Off to Close有的时候,我们需要在用户点击元素之外的时候触发一个事件。最常见的用例是当你想通过点击关闭一个下拉框或对话框时。这是一个必不可少的包,几乎在我构建的每个应用中都会用到。首选:vue-clickawayhttps://github.com/si…

this.$router.push如何刷新页面_小程序丨微信小程序如何实现页面下拉刷新

微信小程序蕴含着众多功能,本期将简单介绍实现页面下拉刷新的方法,通过阅读本文,读者们可以自行动手操作,在实践中认识微信小程序。首先,我们需在json配置中写出以下配置:"enablePullDownRefresh"…

C语言-字符串处理函数strcmp

strcmp-字符串比较函数 原型:int strcmp(const char firststring[], const char secondstring); 功能:比较两个字符串firststring和secondstring 如果等于 返回值为0 如果字符串1大于字符串2 函数值返回为1 如果字符串1小于字符串2 函数值返回为-1 …

导出excel数字前面的0消失_Excel操作中常见的3大坑你遇到过吗?遇到应该这么解决...

在excel中的我们获取数据的方式一是自己录入数据,二是通过网络或其他的途径导出的源数据,不管哪种的方式,对我们处理数据的来说都十分重要,这里我们需要注意这些点并成功跳过这些不按原理只按自己习惯的坑。今天要聊的坑有3个&…

matplot绘制图形入门

一、折线图 """ File: 折线图.py Author: chde_wang Date: 2021-05-23 22:26:04 Description: """ # 绘制折线图 import numpy as np import matplotlib.pyplot as plt x np.linspace(0, 2 * np.pi, 100) y1, y2 np.si…

kafka使用_Kafka介绍与使用

最近在研究kafka,觉得需要输出点东西才能更好的吸收,遂总结与大家分享,话不多说。一、先上思维导图:二、再上kafka整体架构图:2.1、Producer:消息生产者,就是向kafka broker发消息的客户端。2.2…

int 为什么是2147483647_int范围为啥是-2147483648到2147483647

单位换算 1 Byte(B) 8 bit 1 Kilo Byte(KB) 1024B 1 Mega Byte(MB) 1024 KB 1 Giga Byte (GB) 1024 MB 1 Tera Byte(TB) 1024 GB 1 Peta Byte(PB) 1024 TB 从上可以了解计算机最小的存储单位是bit,即一位二进制数(0 或 1),8个二进制位为一个字…

linux 关闭端口_手把手教你在Linux中快速检测端口的 3 个小技巧

一个执着于技术的公众号前言 无论是要解决网络连接问题还是配置防火墙,第一件事是要检查系统实际打开了哪些端口。本文介绍了几种快速查找 Linux 系统上哪些端口向外部开放的方法。什么是开放端口 监听端口是应用程序监听的网络端口。你要得到的监听端口名单通常可以…

2021-01-12

系统设计-设计思路 定义结构体来保存通讯录中联系人的各项信息 使用主菜单实现对通讯录主要功能的管理,不同功能对应着不同的编号 使用结构体数据来存储多个联系人 采用文件来保存联系人的数据,提高操作效率 系统的数据输入包括输入功能编号以及联系…