摘要

本文从synchronized的基本用法,结合对象的内存布局,深入探究synchronized的锁升级的过程,探究其实现原理。准备开始~

synchronized的基础用法

  1. 修饰普通同步⽅方法

    对于非static的情况,synchronized是对象级别的,其实质是将synchronized作用于对象引用(object reference)上,即拿到p1对象锁的线程,对的fun()方法有同步互斥作用,不同的对象之间坚持“和平共处”。

    // 锁的对象是方法的调用者
    public synchronized void method(){ }

    上边的示例代码等同于如下代码:

    public void method() {
    synchronized (this) { }
    }
  2. 修饰静态同步方法

    如果方法用static修饰,synchronized的作用范围就是class一级的,它对类的所有对象起作用。

    Class Foo{
    public static synchronized void method1() {
    }
    }

    上边的示例代码等同于如下代码:

    public void method2()
    {
    synchronized(Foo.class) {
    // class literal(类名称字面常量)
    //请注意,Foo.class也是一个对象,类型是Class,在一个ClassLoader里,它是唯一的。
    }
    }
  3. 修饰同步代码块

    锁就是so这个对象,谁拿到这个锁谁就能够运行他所控制的那段代码。

    同步块,示例代码如下:

    public void method(SomeObject so) {
    synchronized(so)
    {
    //…..
    }
    }

    当有一个明确的对象作为锁时,就能够这样写代码,但当没有明确的对象作为锁,只是想让一段代码同步时,能够创建一个特别的instance变量(它得是个对象)来充当锁

    class Foo implements Runnable{
    private byte[] lock = new byte[0]; // 特别的instance变量
    Public void method(){
    synchronized(lock) { }
    }
    • 注:零长度的byte数组对象创建起来将比任何对象都经济。查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

特殊说明

  • 使⽤用synchronized修饰类和对象时,由于类对象和实例例对象分别拥有⾃自⼰己的监视器器锁,因此不不会
    相互阻塞
  • 使⽤synchronized修饰实例例对象时,如果一个线程正在访问实例例对象的一个synchronized方法时,其它线程不仅不能访问该synchronized⽅法,该对象的其它synchronized方法也不不能访问,因为一个对象只有一个监视器器锁对象,但是其它线程可以访问该对象的非synchronized⽅法。

synchronized原理

字节码理解

在说synchronized原理时,就不得不先了解一下Monitor

认识 Java Monitor Object

Java Monitor 从两个方面来支持线程之间的同步,即:互斥执行(对象内的所有方法都互斥的执行。好比一个 Monitor 只有一个运行许可,任一个线程进入任何一个方法都需要获得这个许可,离开时把许可归还)与协作(通常提供signal机制:允许正持有许可的线程暂时放弃许可,等待某个监视条件成真,条件成立后,当前线程可以通知正在等待这个条件的线程,让它可以重新获得运行许可)。 Java 使用对象锁 ( 使用 synchronized 获得对象锁 ) 保证工作在共享的数据集上的线程互斥执行 , 使用 notify/notifyAll/wait 方法来协同不同线程之间的工作。这些方法在 Object 类上被定义,会被所有的 Java 对象自动继承。

Java 语言对于这样一个典型并发设计模式做了内建的支持,线程如果获得监视锁成功,将成为该监视者对象的拥有者。在任一时刻内,监视者对象只属于一个活动线程 (Owner) 。拥有者线程可以调用 wait 方法自动释放监视锁,进入等待状态。下图很好地描述了 Java Monitor 的工作机理。

在Java虚拟机(HotSpot)中, monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp⽂文件,C++实现的),位于/openjdk-8u60/hotspot/src/share/vm/runtime/objectMonitor.hpp

ObjectMonitor() {
_header = NULL; //markOop对象头
_count = 0; // 可以⼤大于1,可重⼊入
_waiters = 0, //等待线程数
_recursions = 0; //重⼊入次数
_object = NULL; //监视器器锁寄⽣生的对象。锁不不是平⽩白出现的,⽽而是寄托存储于对象中。
_owner = NULL; //初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯⼀一标识,当锁被释放时⼜又设置为NULL
_WaitSet = NULL; //处于wait状态的线程,会被加⼊入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //处于等待锁block状态的线程,会被加⼊入到该列列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}

每个线程都有两个ObjectMonitor对象列列表,分别为free和used列列表,如果当前free列列表为空,线程将
向全局global list请求分配ObjectMonitor。thread结构/openjdk-8u60/hotspot/src/share/vm/runtime/thread.hpp

  // Private thread-local objectmonitor list - a simple cache organized as a SLL.
public:
ObjectMonitor* omFreeList;
int omFreeCount; // length of omFreeList
int omFreeProvision; // reload chunk size
ObjectMonitor* omInUseList; // SLL to track monitors in circulation
int omInUseCount; // length of omInUseList

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

由此看来, monitor对象存在于每个Java对象的对象头中(存储的指针的指向), synchronized锁便是通过这种⽅式获取锁的,也是为什么Java中任意对象可以作为锁的原因。


代码验证

是不是一脸崩?不知道在说什么

温故知新-多线程-深入刨析synchronized的更多相关文章

  1. 温故知新-多线程-深入刨析volatile关键词

    文章目录 摘要 volatile的作用 volatile如何解决线程可见? CPU Cache CPU Cache & 主内存 缓存一致性协议 volatile如何解决指令重排序? volat ...

  2. 温故知新-多线程-深入刨析CAS

    文章目录 摘要 CAS是什么? CAS是如何实现的? CAS存在的问题? 你的鼓励也是我创作的动力 Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 ...

  3. 温故知新-多线程-深入刨析park、unpark

    文章目录 摘要 park.unpark 看一下hotspot实现 参考 你的鼓励也是我创作的动力 Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | ...

  4. 温故知新-多线程-Cache Line存在验证

    文章目录 简述 缓存行Cache Line 验证CacehLine存在? 参考 你的鼓励也是我创作的动力 Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持 ...

  5. 深入刨析tomcat 之---第8篇 how tomcat works 第11章 11.9应用程序,自定义Filter,及注册

    writed by 张艳涛, 标签:全网独一份, 自定义一个Filter 起因:在学习深入刨析tomcat的学习中,第11章,说了调用过滤链的原理,但没有给出实例来,自己经过分析,给出来了一个Filt ...

  6. 深入刨析tomcat 之---第2篇,解决第3章bug 页面不显示内容http://localhost:8080/servlet/ModernServlet?userName=zhangyantao&password=1234

    writedby 张艳涛7月2日, 在学习第4张的过程中,发现了前一篇文章写的是关于1,2张的bug不用设置response响应头,需要在servlet的service()方法里面写是错误想 serv ...

  7. Orchard 刨析:Logging

    最近事情比较多,有预研的,有目前正在研发的,都是很需要时间的工作,所以导致这周只写了两篇Orchard系列的文章,这边不能保证后期会很频繁的更新该系列,但我会写完这整个系列,包括后面会把正在研发的东西 ...

  8. Orchard 刨析:Caching

    关于Orchard中的Caching组件已经有一些文章做了介绍,为了系列的完整性会再次对Caching组件进行一次介绍. 缓存的使用 在Orchard看到如下一段代码: 可以看到使用缓存的方法Get而 ...

  9. Orchard 刨析:导航篇

    之前承诺过针对Orchard Framework写一个系列.本应该在昨天写下这篇导航篇,不过昨天比较累偷懒的去玩了两盘单机游戏哈哈.下面进入正题. 写在前面 面向读者 之前和本文一再以Orchard ...

随机推荐

  1. PAT 1007 Maximum Subsequence Sum (25分)

    题目 Given a sequence of K integers { N​1​​ , N​2​​ , ..., N​K​​ }. A continuous subsequence is define ...

  2. 笨办法学习python-ex41源码加自己注释

    #!/user/bin/env python #-*-coding:utf-8 -*- #Author: qinjiaxi import random from urllib import urlop ...

  3. 浅谈PostgreSQL用户权限

    问题 经常在PG群里看到有人在问“为什么我对表赋予了权限:但是还是不能访问表” 解析 若你看懂德哥这篇文章PostgreSQL逻辑结构和权限体系介绍:上面对你就不是困扰你的问题 解决这个问题很简单:在 ...

  4. 从模块化到认识Babel

    转载自:https://www.cnblogs.com/qcloud1001/p/10167756.html https://blog.csdn.net/a250758092/article/deta ...

  5. sound of the genuine

    “There is something in every one of you that waits and listens for the sound of the genuine in yours ...

  6. day05:数组与字典常识(20170217)

    #1:数组功能的使用:print ("a4A".isdecimal()) #print ("18".isdigit()) #判断是否是数字print (&quo ...

  7. Hyperledger Fabric——balance transfer(二)注册用户

    详细分析blance transfer示例的用户注册(register)与登录(enroll)功能. 源码分析 1.首先分析项目根目录的app.js文件中关于用户注册和登录的路由函数.注意这里的tok ...

  8. SPL常用迭代器

    ArrayIterator 熟悉使用seek()跳过元素 熟悉使用asort,ksort排序 <?php $fruits = array( 'apple'=>'apple value', ...

  9. WordPress美化百度分享默认图标

    因代码中使用了Font Awesome字体图标,如果你的主题没有加载字体图标,可以到WP后台--插件--安装插件页面搜索:Font Awesome 4 Menus 安装并启用,才能显示替换后的图标. ...

  10. JS轮播图带序号小点和左右按钮

    轮播图作为前端比较简易的动画,使用非常频繁,这里记录以便使用 此轮播图为最简易自动播放,非无缝,但有按钮,有序号跳转小点 想看全套轮播图可以查看我的分类轮播图全套 html布局 <div sty ...