基础概念

  synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时可以保证共享变量对内存可见性。

  Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:

    1. 普通同步方法,锁是当前实例对象
    2. 静态同步方法,锁是当前类的class对象
    3. 同步方法块,锁是括号里面的对象

  当一个线程访问同步代码块时,它首先是需要得到锁才能执行同步代码,当退出或者抛出异常时必须要释放锁。

底层实现

如何来实现这个机制呢?我们先看如下一段简单代码:

public class SynchronizedTest{
public synchronized void test1(){ }
public void test2(){
synchronized(this){
}
} public static void main(String []args){ }
}

利用javap工具查看生成的class 文件信息来分析synchronize的实现

  从上图可以看出,同步代码块是使用monitorenter和monitorexit指令实现的,同步方法(在这看不出来需要看JVM底层实现)依靠的是方法修饰符上的ACC_SYNCHRONIZED实现。

  在java语言中存在两种内建的synchronized语法:1、synchronized语句;2、synchronized方法。对于synchronized语句当Java源代码被javac编译成bytecode的时候,会在同步块的入口位置和退出位置分别插入monitorenter和monitorexit字节码指令。而synchronized方法则会被翻译成普通的方法调用和返回指令如:invokevirtual、areturn指令,在VM字节码层面并没有任何特别的指令来实现被synchronized修饰的方法,而是在Class文件的方法表中将该方法的access_flags字段中的synchronized标志位置1,表示该方法是同步方法并使用调用该方法的对象或该方法所属的Class在JVM的内部对象表示Klass做为锁对象。

Java synchronized 包含两方面的含义

互斥

JVM 通过对象锁来实现互斥:

typedef struct object {
uintptr_t lock;
Class *class;
} Object

协作
协作是通过 Object wait, notify/notifyAll 方法实现的。

对应到JVM 的底层术语,这一机制叫做 monitor:

typedef struct monitor {
pthread_mutex_t lock;
Thread *owner;
Object *obj;
int count;
int in_wait;
uintptr_t entering;
int wait_count;
Thread *wait_set;
struct monitor *next;
} Monitor;

在底层,进行获取 lock 动作到获得 lock 之间有一小段状态叫做 BLOCKED:

void monitorLock(Monitor *mon, Thread *self) {
if(mon->owner == self)
mon->count++;
else {
if(pthread_mutex_trylock(&mon->lock)) {
disableSuspend(self); self->blocked_mon = mon;
self->blocked_count++;
self->state = BLOCKED; pthread_mutex_lock(&mon->lock); self->state = RUNNING;
self->blocked_mon = NULL; enableSuspend(self);
}
mon->owner = self;
}
}

下面我们来了解两个重要的概念:Java对象头,Monitor。

对象头(Object Header):

  在JVM中创建对象时会在对象前面加上两个字大小的对象头,在32位机器上一个字为32bit,根据不同的状态位Mark World中存放不同的内容,如上图所示在轻量级锁中,Mark Word被分成两部分,刚开始时LockWord为被设置为HashCode、最低三位表示LockWord所处的状态,初始状态为001表示无锁状态。Klass ptr指向Class字节码在虚拟机内部的对象表示的地址。Fields表示连续的对象实例字段。

Monitor Record:

   Monitor Record是线程私有的数据结构,每一个线程都有一个可用monitor record列表,同时还有一个全局的可用列表;那么这些monitor record有什么用呢?每一个被锁住的对象都会和一个monitor record关联(对象头中的LockWord指向monitor record的起始地址,由于这个地址是8byte对齐的所以LockWord的最低三位可以用来作为状态位),同时monitor record中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。如下图所示为Monitor Record的内部结构:

Owner:初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL;

EntryQ:关联一个系统互斥锁(semaphore),阻塞所有试图锁住monitor record失败的线程。

RcThis:表示blocked或waiting在该monitor record上的所有线程的个数。

Nest:用来实现重入锁的计数。

HashCode:保存从对象头拷贝过来的HashCode值(可能还包含GC age)。

Candidate:用来避免不必要的阻塞或等待线程唤醒,因为每一次只有一个线程能够成功拥有锁,如果每次前一个释放锁的线程唤醒所有正在阻塞或等待的线程,会引起不必要的上下文切换(从阻塞到就绪然后因为竞争锁失败又被阻塞)从而导致性能严重下降。Candidate只有两种可能的值0表示没有需要唤醒的线程1表示要唤醒一个继任线程来竞争锁。

深入分析synchronized的实现原理的更多相关文章

  1. 【死磕Java并发】-----深入分析synchronized的实现原理

    记得刚刚開始学习Java的时候.一遇到多线程情况就是synchronized.相对于当时的我们来说synchronized是这么的奇妙而又强大,那个时候我们赋予它一个名字"同步". ...

  2. Java并发—–深入分析synchronized的实现原理

    记得刚刚开始学习Java的时候,一遇到多线程情况就是synchronized,相对于当时的我们来说synchronized是这么的神奇而又强大,那个时候我们赋予它一个名字“同步”,也成为了我们解决多线 ...

  3. 深入分析Synchronized原理(阿里面试题)

    还有一篇 讲解lock的实现原理,参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理以及如何 获取锁AQS算法 (百度-美团) 记得开始学习Java的时候,一遇到多线程 ...

  4. 深入分析Synchronized原理

    前言 记得开始学习Java的时候,一遇到多线程情况就使用synchronized,相对于当时的我们来说synchronized是这么的神奇而又强大,那个时候我们赋予它一个名字“同步”,也成为了我们解决 ...

  5. 聊聊并发(一)深入分析Volatile的实现原理

    本文属于作者原创,原文发表于InfoQ:http://www.infoq.com/cn/articles/ftf-java-volatile 引言 在多线程并发编程中synchronized和Vola ...

  6. Java并发编程:Synchronized及其实现原理

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  7. Synchronized及其实现原理

    并发编程中synchronized一直是元老级角色,我们称之为重量级锁.主要用在三个地方: 1.修饰普通方法,锁是当前实例对象. 2.修饰类方法,锁是当前类的Class对象. 3.修饰代码块,锁是sy ...

  8. synchronized底层实现原理&CAS操作&偏向锁、轻量级锁,重量级锁、自旋锁、自适应自旋锁、锁消除、锁粗化

    进入时:monitorenter 每个对象有一个监视器锁(monitor).当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:1 ...

  9. synchronized的实现原理与应用

    Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令. sync ...

随机推荐

  1. CSS <input type="file">样式设置

    这是最终想要的效果~~~ 实现很简单,div设置背景图片,<input type="file"/>绝对定位上去再设置opacity:0(透明度为0 ) 直接上代码,希望 ...

  2. JAVA_SE基础——8.基本数据类型

    基本数据类型有:整数类型.浮点类型.字符类型.布尔类型 整数类型 整数类型用来存储整数数值,即没有小数部分的数值.与C.C++语言相同,整数在Java语言中有3种表示形式:十进制.八进制和十六进制. ...

  3. Spring源码情操陶冶#task:scheduled-tasks解析器

    承接前文Spring源码情操陶冶#task:executor解析器,在前文基础上解析我们常用的spring中的定时任务的节点配置.备注:此文建立在spring的4.2.3.RELEASE版本 附例 S ...

  4. 微信小程序如何动态增删class类名

    简述 由于微信小程序开发不同于以往的普通web开发, 因此无法通过js获取wxml文件的dom结构, 因此从js上直接添加一个类名应该不可能了. 可是我们可以通过微信小程序数据绑定以及view标签的& ...

  5. 以太坊挖矿源码:clique算法

    上文我们总结了以太坊最主要的共识算法:ethash算法,本文将重点分析以太坊的另一个共识算法:clique. 关键字:clique,共识算法,puppeth,以太坊地址原理,区块校验,认证结点,POA ...

  6. QT5.5与MYSQL5.6数据库连接的具体方法与实现

    由于毕设需要用到QT读取数据库中的数据,并将数据保存至数据库中.花了一天的时间,总算实现了从QT中读取数据库中的数据.网上相关资料很多,但是写得不是很全,中间出现了一些问题,解决起来比较麻烦.所以本文 ...

  7. Excel 日期截取(函数)

    需求:时间段截取,去掉年月日,保留时分. 实现函数:    =TEXT(A2,"HH:MM")&"-"&TEXT(B2,"HH:MM& ...

  8. PyMySQL模块的使用

    PyMySQL介绍 PyMySQL是在Python3.x版本中用于连接MySQL服务器的一个库,Python2系列中则使用mysqldb.Django中也可以使用PyMySQL连接MySQL数据库. ...

  9. JavaScript 重点笔记

    JavaScript 重点笔记 ## 数组 // 必须掌握 - arr.length:获取数组元素的长度 - arr.splice(起始位置,长度):从数组中添加或删除元素. - arr.indexO ...

  10. python中关于文件的读取和写入

    open()和close()方法:使用python的内置函数open()打开一个文件,创建一个file对象,相关的方法才可以调用它进行读写. file object = open(file_name ...