重新认识synchronized(下)
synchronized既保证原子性,又保证内存可见性,是一种线程同步的方式,是锁机制的一种java实现。synchronized的实现基于JVM底层,JVM是基于monitor实现的,而monitor的实现依赖于操作系统的互斥实现。
语义
synchronized语义是同步,但同步有两层含义:
- 互斥,即锁的特点。同一时间只能有一个线程持有监视器,因此一旦线程进入监视器保护的代码块(即临界区),其他线程是不允许监视器保护的代码块,直到前一个线程退出代码块。互斥阻止了其他线程看到对象不一致的状态,与原子性有相同的语义。
- 可见。synchronized保证进入同步代码块的线程,都可以看到由同一个锁保护的之前所有的修改效果。原因是:在释放监视器时(即退出同步代码块),会将工作内存中未映射到主内存的工作拷贝,强制刷新回主内容。在获取监视器是(即进入同步代码块时),监视器会使本地内存失效,强制从主内存拷贝到工作内存。
互斥保证在线程退出前,所有对象状态变更都对其他线程不可见;可见保证在线程进入同步代码块时,可以看到上一个线程对对象状态变更的最终状态。
线程安全与同步
线程安全表明在多线程环境中,不会有多个线程同时访问共享数据。
线程同步是线程访问类和实例字段变量,和其他共享资源的一种串行化行为,确保在同一时间只能有一个线程访问资源。举个栗子,春运火车票只剩下最后一张火车票,A,B都要抢这张火车票,怎么解决这个问题防止超卖呢?把资源保护起来,让A,B排队来买火车票。
线程安全是属性,线程同步是方式。
指令
synchronized同步代码块是通过monitorenter和monitorexit指令实现的,而synchronized同步方法是基于ACC_SYCHRONIZED标志,同步方法被调用时JVM会检查这个标志。monitorenter标记临界区的开始,线程执行到 monitorenter 指令时,将会尝试获取对象所对应的 monitor 的所有权;monitorexit标记临界区的结束,线程执行到 monitorexit 指令时,将释放对象所对应的 monitor 的所有权。
public class SynchronizedMethod {
public synchronized void methodA() {
System.out.println("MethodA start"); }
}
将这段代码通过 javap -c
反编译一下,重点关注一下编译后的第3行和第13行。
Compiled from "SynchronizedTest.java"
public class com.memory.SynchronizedTest {
public com.memory.SynchronizedTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return public void methodA();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String MethodA start
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit
14: goto 22
17: astore_2
18: aload_1
19: monitorexit
20: aload_2
21: athrow
22: return
Exception table:
from to target type
4 14 17 any
17 20 17 any
}
锁的种类
java synchronized锁升级
JDK1.6中对synchronized优化引入了偏向锁,轻量级锁,重量级锁。锁的升级过程是单方向的,只允许从低到高升级,不允许降级。
重量级锁(Heavyweight Lock)是将程序运行交出控制权,将线程挂起,由操作系统来负责线程间的调度,负责线程的阻塞和执行。这样会出现频繁地对线程运行状态的切换,线程的挂起和唤醒,消耗大量的系统资源,导致性能低下。
轻量级锁(lightweight Locking)是相对于重量级锁而言的,在synchronized实现中使用自旋的方式,实际是通过CPU自旋等待的方式替代线程切换,竞争的线程不会因此而阻塞,避免阻塞唤醒造成的CPU负荷。采用自旋的方式有利有弊,当锁占用的时间较短时,较少次数的自旋等待就可以获取锁;但在锁占用的时间较长时,自旋会白白浪费大量的CPU资源。因此自旋的次数有一定要在限定之内,自旋失败就会立即将锁升级为重量级锁,称为锁膨胀。
偏向锁(Biased Locking )从字面含义是这把锁是有私心的,会倾向于上次访问的线程。Hotspot的作者在他的论文《QRL-OpLocks-BiasedLocking》中阐述到,研究发现大多数情况下不存在多线程争夺共享资源,而且总是由同一线程多次获得,考虑到CAS (Compare-And-Swap)指令在获取Java监视器时会造成较大的CPU延迟,为了让线程获得锁的代价更低而引入了偏向锁。
锁标记的位置
64位虚拟机中,标记字段(Mark Word)中包含哈希吗(HashCode,存放31bits对象的hashcode值),GC分代年龄(Generational GC Age,4bits,因此分代年龄最高为15),偏向线程ID,偏向锁标记。
synchronized锁的四个状态:无锁状态,偏向锁,轻量级锁和重量级锁,在Mark Word中对应不同的字段。
java synchronized不同级别锁中的Mark Word
我是葛一凡,希望对你有用。
参考
- JSR 133 (Java Memory Model) FAQ
- Java内存模型(一)
- Java 锁机机制——浅析 Synchronized
- Biased Locking in HotSpot
- Java Synchronised机制
- 深入JVM锁机制1-synchronized
- 多线程之:偏向锁,轻量级锁,重量级锁
- JVM内部细节之一:synchronized关键字及实现细节(轻量级锁Lightweight Locking)
- java锁优化
- Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)
- 虚拟机中的锁优化简介(适应性自旋/锁粗化/锁削除/轻量级锁/偏向锁)
- JVM内部细节之二:偏向锁(Biased Locking)
- 小议偏向锁
- synchronized、锁、多线程同步的原理是咋样的
- Java中的锁
- markOop.hpp
- 5 Things You Didn’t Know About Synchronization in Java and Scala
- 论文:The Smart Energy Management of Multithreaded Java Applications on Multi-Core Processors
重新认识synchronized(下)的更多相关文章
- 内置锁(二)synchronized下的等待通知机制
一.等待/通知机制的简介 线程之间的协作: 为了完成某个任务,线程之间需要进行协作,采取的方式:中断.互斥,以及互斥上面的线程的挂起.唤醒:如:生成者--消费者模式.或者某个动作完成,可以唤醒下一 ...
- synchronized下的 i+=2 和 i++ i++执行结果居然不一样
起因 逛[博客园-博问]时发现了一段有意思的问题: 问题链接:https://q.cnblogs.com/q/140032/ 这段代码是这样的: import java.util.concurrent ...
- 深入浅出Java并发包—锁机制(一)
前面我们看到了Lock和synchronized都能正常的保证数据的一致性(上文例子中执行的结果都是20000000),也看到了Lock的优势,那究竟他们是什么原理来保障的呢?今天我们就来探讨下Jav ...
- Java并发知识整理
整理了一下前段时间学习Java并发的笔记,大约有40篇. 1. Java并发基础知识 并发基础(一) 线程介绍 并发基础(二) Thread类的API总结 并发基础(三) java线程优先级 并发基础 ...
- Java 知识笔记 - 类、集合、多线程、IO、JVM(最后一次更新,2019年02月17日)
目录 Class 内部类.静态内部类.匿名内部类.局部内部类 Collection Java Collection Set Queue Map Collections Arrays System Co ...
- CAS及其ABA问题
CAS.volatile是JUC包实现同步的基础.Synchronized下的偏向锁.轻量级锁的获取.释放,lock机制下锁的获取.释放,获取失败后线程的入队等操作都是CAS操作锁标志位.state. ...
- Java-线程等待、唤醒与中断
一.sleep() 与 wait() 两者都会让当前线程进入等待状态.唤醒后都需要等待 CPU 资源,不一定会立即执行.若在等待期间被调用此线程的的 interrupt() 方法,将会产生 Inter ...
- JUC——检视阅读
JUC--检视阅读 参考资料 JUC知识图参考 JUC框架学习顺序参考 J.U.C学习总结参考,简洁直观 易百并发编程,实践操作1,不推荐阅读,不及格 JUC文章,带例子讲解,可以学习2 Doug L ...
- C++程序结构---1
C++ 基础教程Beta 版 原作:Juan Soulié 翻译:Jing Xu (aqua) 英文原版 本教程根据Juan Soulie的英文版C++教程翻译并改编. 本版为最新校对版,尚未定稿.如 ...
- 并发情况下synchronized死锁
存在缺陷的代码: public class DataPropertyIdAndNameRepositoryImpl{ /** 发布标志 */ private volatile boolean publ ...
随机推荐
- 关于CKEDITOR的一些小问题
<textarea name="tMessage" ></textarea> <script type="text/javascript& ...
- HBase-存储-文件
文件 HBase使用一个HDFS中可配置的根目录,默认设为“/hbase”.可使用hadoop fs -lsr /hbase查看目录结构,文件可以被分为两类,一类位于HBase根目录下,另一类位于根目 ...
- 用intellij idea 写第一个Java程序
Java小白,还不怎么会eclipse,只会在命令行用javac编译并java运行编译后的类. 英文还不好orz 发现创建项目后,能build但就是不能run... 找了半天教程没找着,去官网溜了一下 ...
- Installing StackTach
为StackTach创建database,默认使用MySql,也可以 在settings.py 文件中配置其他的. create stack db mysql -u root -p mysql> ...
- review14
日期的格式化 Formatter类的format方法: format(格式化模式, 日期列表) 按着“格式化模式”返回“日期列表”中所列各个日期中所含数据(年,月,日,小时等数据)的字符串表示.“格式 ...
- 解析Ceph: 数据的端到端正确性和 Scrub 机制
转自:https://www.ustack.com/blog/ceph-internal-scrub/ Ceph 的主要一大特点是强一致性,这里主要指端到端的一致性.众所周知,传统存储路径上从应用层到 ...
- es6基础入门变量的解构赋值
let [a, b, c] = [1, 2, 3]; let [foo, [[bar], baz]] = [1, [[2], 3]]; foo bar baz let [ , , third] = [ ...
- Servlet细节处理
1.Servlet的线程安全问题 尽管servlet的工作效率高,但是线程也是不安全的,因为一个类型的Servlet只有一个实例对象,那么就有可能会出现一个Servlet同时处理多个请求 解决方案:所 ...
- nyoj-130-相同的雪花(hash)
题目链接 /* Name:NYOJ-130-相同的雪花 Copyright: Author: Date: 2018/4/14 15:13:39 Description: 将雪花各个分支上的值加起来,h ...
- PhotoShop使用指南(3)—— 将多张图片添加到图层
第一步:选择文件菜单>脚本>将文件载入堆栈 第二步:点击浏览添加要批量载入的图片