java并发编程:深入了解synchronized
简介
synchronized是Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。同时它还保证了共享变量的内存可见性。
synchronized使用
synchronized可以修饰普通方法,静态方法和代码块。
- 普通同步方法,锁是当前实例对象(不同实例对象之间的锁互不影响)。
- 静态同步方法,锁是当前类的class对象
- 同步方法块,锁是括号里面的对象
synchronized实现的原理
synchronized的功能是基于monitorenter和monitorexit指令实现的。monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。
什么是monitor
- Java Monitor 相当于监视器,一把打开大门的钥匙,也可认为是一个许可证。只有拿到许可证,才可以操作。
- 同时也相当于一个同步工具,操作系统中的互斥量(mutex),值为1的信号量。
synchronized对锁的优化
由于JDK之前的版本 synchronized性能比较差,JDK 1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。
锁粗化
也就是减少不必要的紧连在一起的unlock,lock操作,将多个连续的锁扩展成一个范围更大的锁。
锁消除
由来
为了保证数据的完整性,我们在进行操作时需要对这部分操作进行同步控制。但是,在有些情况下,JVM检测到不可能存在共享数据竞争,这是JVM会对这些同步锁进行锁消除。如果不存在竞争,为什么还需要加锁呢?所以锁消除可以节省毫无意义的请求锁的时间。
原理
锁消除的依据是逃逸分析的数据支持。变量是否逃逸,对于虚拟机来说需要使用数据流分析来确定,但是对于我们程序员来说这还不清楚么?我们会在明明知道不存在数据竞争的代码块前加上同步吗?但是有时候程序并不是我们所想的那样?我们虽然没有显示使用锁,但是我们在使用一些 JDK 的内置 API 时,如 StringBuffer、Vector、HashTable 等,这个时候会存在隐性的加锁操作。
逃逸分析是java虚拟机比较前言的优化技术。它并不是直接的优化技术的手段,而是为其他优化技术手段提供依据。
逃逸分析,主要是分析对象的动态作用范围,比如在一个方法里一个对象创建后,在调用外部方法时,该对象作为参数传递到其他方法中,成为方法逃逸;当被其他线程访问,如赋值给其他线程中的实例变量,则成为线程逃逸。
如果可以证明一个对象不会出现方法或者线程逃逸,也就是说别的方法或者线程无法访问到这个对象,则可以对这个对象做优化处理。
逃逸分析的 JVM 参数如下:
开启逃逸分析:-XX:+DoEscapeAnalysis
关闭逃逸分析:-XX:-DoEscapeAnalysis
显示分析结果:-XX:+PrintEscapeAnalysis
逃逸分析技术在 Java SE 6u23+ 开始支持,并默认设置为启用状态,可以不用额外加这个参数。
锁粗化 & 锁消除 栗子
public synchronized StringBuffer append(int i) {
toStringCache = null;
super.append(i);
return this;
}
/**
* 锁粗化
*/
public static String synchronizedCoarseningDemo(StringBuffer sb){
//JVM 检测到对同一个对象(StringBuffer)连续加锁、解锁操作,
// 会合并一个更大范围的加锁、解锁操作,即加锁解锁操作会移到 for 循环之外。
for (int i = 0; i < 1000000; i++) {
sb.append(i);
sb.append("&");
}
return sb.toString();
}
/**
* 锁消除
*/
public static String synchronizedEliminationDemo(){
//jvm通过逃逸分析判断 对象StringBuffer,不存在锁竞争,
// 所以会把append方法加锁(synchronized)操作去掉
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 1000000; i++) {
sb.append(i);
sb.append("&");
}
return sb.toString();
}
自旋锁
由来
线程的阻塞和唤醒,需要 CPU 从用户态转为核心态。频繁的阻塞和唤醒对 CPU 来说是一件负担很重的工作,势必会给系统的并发性能带来很大的压力。同时,我们发现在许多应用上面,对象锁的锁状态只会持续很短一段时间。为了这一段很短的时间,频繁地阻塞和唤醒线程是非常不值得的
定义
所谓自旋锁,就是让该线程等待一段时间,不会被立即挂起,看持有锁的线程是否会很快释放锁。
自旋等待不能替代阻塞,先不说对处理器数量的要求(多核,貌似现在没有单核的处理器了),虽然它可以避免线程切换带来的开销,但是它占用了处理器的时间。如果持有锁的线程很快就释放了锁,那么自旋的效率就非常好,反之,自旋的线程就会白白消耗掉处理的资源,它不会做任何有意义的工作,典型的占着茅坑不拉屎,这样反而会带来性能上的浪费。
所以说,自旋等待的时间(自旋的次数)必须要有一个限度,如果自旋超过了定义的时间仍然没有获取到锁,则应该被挂起。
自旋锁在 JDK 1.4.2 中引入,默认关闭,但是可以使用 -XX:+UseSpinning 开开启。
在 JDK1.6 中默认开启。同时自旋的默认次数为 10 次,可以通过参数 -XX:PreBlockSpin 来调整。
适应自旋锁
JDK 1.6引入了更加聪明的自旋锁,即自适应自旋锁。所谓自适应就意味着自旋的次数不再是固定的,它是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。
轻量级锁
引入轻量级锁的主要目的是:在多线程竞争不激烈的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。需要注意的是轻量级锁并不是取代重量级锁,而是在大多数情况下同步块并不会出现严重的竞争情况,所以引入轻量级锁可以减少重量级锁对线程的阻塞带来的开销。
偏向锁
引入偏向锁的主要目的是:为了在无多线程竞争的情况下尽量减少不必须要的轻量级锁执行路径。其实在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获取,所以引入偏向锁就可以减少很多不必要的性能开销和上下文切换。
锁的升级/锁膨胀
synchronized 同步锁一共具有四种状态:无锁、偏向锁、轻量级锁、重量级锁,他们会随着竞争情况逐渐升级,此过程为不可逆。所以 synchronized 锁膨胀过程其实就是无锁 → 偏向锁 → 轻量级锁 → 重量级锁的一个过程。
注意,锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。
锁膨胀原理地址
synchronized 共享变量的内存可见性
java内存模型对加锁&解锁的两个规定
1、线程解锁前,必须把共享变量的最新值刷新到主内存中;
2、线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量需要从主内存中重新读取最新的值(加锁与解锁需要统一把锁)
线程执行互斥锁代码的过程:
1.获得互斥锁
2.清空工作内存
3.从主内存拷贝最新变量副本到工作内存
4.执行代码块
5.将更改后的共享变量的值刷新到主内存中
6.释放互斥锁
参考链接:
芋道源码
synchronized:【
http://cmsblogs.com/?p=5812
https://www.cnblogs.com/54chensongxia/p/11899031.html
https://www.cnblogs.com/javaminer/p/3889023.html
http://cmsblogs.com/?p=2071
】
逃逸分析: https://blog.csdn.net/u010503427/article/details/89188013
内存可见性 :https://blog.csdn.net/ArtisticProgramming/article/details/107103934
java并发编程:深入了解synchronized的更多相关文章
- 【Java并发编程实战】-----synchronized
在我们的实际应用当中可能经常会遇到这样一个场景:多个线程读或者.写相同的数据,访问相同的文件等等.对于这种情况如果我们不加以控制,是非常容易导致错误的.在java中,为了解决这个问题,引入临界区概念. ...
- 干货:Java并发编程系列之synchronized(一)
1. 使用方法 synchronized 是 java 中最常用的保证线程安全的方式,synchronized 的作用主要有三方面: 确保线程互斥的访问代码块,同一时刻只有一个方法可以进入到临界区 保 ...
- 【Java并发编程实战】—–synchronized
在我们的实际应用其中可能常常会遇到这样一个场景:多个线程读或者.写相同的数据,訪问相同的文件等等.对于这样的情况假设我们不加以控制,是非常easy导致错误的. 在java中,为了解决问题,引入临界区概 ...
- Java并发编程-深入探讨synchronized实现原理
synchronized这个关键字对应Java程序猿来说是非常的熟悉,只要遇到要解决线程安全问题的地方都会使用这个关键字.接下来一起来探讨一下synchronized到底时怎么实现线程同步,使用syn ...
- Java 并发编程——volatile与synchronized
一.Java并发基础 多线程的优点 资源利用率更好 程序设计在某些情况下更简单 程序响应更快 这一点可能对于做客户端开发的更加清楚,一般的UI操作都需要开启一个子线程去完成某个任务,否者会容易导致客户 ...
- 干货:Java并发编程系列之volatile(二)
接上一篇<Java并发编程系列之synchronized(一)>,这是第二篇,说的是关于并发编程的volatile元素. Java语言规范第三版中对volatile的定义如下:Java编程 ...
- Java并发编程:synchronized
Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...
- Java并发编程:Synchronized及其实现原理
Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...
- Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)
Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...
随机推荐
- Android学习之 AlertDialog
•AlertDialog简介 AlertDialog 可以在当前界面弹出一个对话框: 这个对话框是置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力: 因此, AlertDialog 一般用于提示 ...
- shiro报错SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".和Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
未能加载类"org.slf4j.impl.StaticLoggerBinder" 解决方案: <dependency> <groupId>org.slf4j ...
- 利用过滤器Filter和特性Attribute实现对Web API返回结果的封装和统一异常处理
在我们开发Web API应用的时候,我们可以借鉴ABP框架的过滤器Filter和特性Attribute的应用,实现对Web API返回结果的封装和统一异常处理,本篇随笔介绍利用AuthorizeAtt ...
- Java高并发测试框架JCStress
前言 如果要研究高并发,一般会借助高并发工具来进行测试.JCStress(Java Concurrency Stress)它是OpenJDK中的一个高并发测试工具,它可以帮助我们研究在高并发场景下JV ...
- 开源组件编排引擎LiteFlow发布里程碑版本2.5.0
介绍 LiteFlow作为一款轻量级组件编排框架,自开源来,获得了挺多人的关注.社区群也扩展到了接近200人. 早期版本因为疏忽打理,有一些BUG,迭代也不及时.距离上一个稳定版本2.3.3,已经有超 ...
- [SIGIR2020] Sequential Recommendation with Self-Attentive Multi-Adversarial Network
这篇论文主要提出了一个网络,成为Multi-Factor Generative Adversarial Network,直接翻译过来的话就是多因子生成对抗网络.主要是期望能够探究影响推荐的其他因子(因 ...
- Struts2(十六篇)
(一)Struts2框架概述 (二)Struts2配置文件 (三)Struts2的Action(简单讲解版) (四)Struts2的Action(深入讲解版) (五)Struts2处理结果管理 (六) ...
- 【pytest官方文档】解读Skipping test functions,跳过测试用例详解
有时候,为了满足某些场景的需要,我们知道有些测试函数在这时候肯定不能执行,或者执行了也会失败.那么我们 可以选择去跳过这个测试函数,这样也就不会影响整体的测试函数运行效果,不至于在你运行的众多绿色通过 ...
- Spring学习笔记(五):JDBCTemplate+事务管理
1 概述 Spring为开发者提供了JDBCTemplate,可以简化很多数据库操作相关的代码,本文主要介绍JDBCTemplate的使用以及事务管理功能. 2 JDBC Template 2.1 配 ...
- 扩展 GRTN:云原生趋势下的 RTC 架构演进
在 2021 LiveVideoStackCon 音视频技术大会上海站,聚焦 "轻端重云和边缘架构新模式" 专场,阿里云视频云的 RTC 传输专家杨成立(忘篱)带来 "基 ...