基本使用

  1. synchronized放在实例方法上,锁对象是当前的this对象
  2. synchronized放在类方法(静态方法)上,锁对象是方法区中的类对象
  3. synchronized修饰代码块,也就是synchronized(object){},锁对象是()中的对象

synchronized用来修饰方法时,是通过ACC_SYNCHRONIZED标识符来保持线程同步的。

synchronized用来修饰代码块时,是通过monitorentermonitorexit指令来完成

实现原理

  • 代码层面, Synchronized 关键字。

  • JAVA 字节码层面,同步方法是 ACC_SYNCHRONIZED 修饰的方法定义,同步代码块使用 monitorentermonitorexit两个指令实现。

  • JVM 层面, 无锁,偏向锁,轻量级锁,重量级锁。

  • 操作系统层面是调用了汇编指令 lock cmpxchg,通过 lock 保证后面的命令(cmpxchg)只能一个线程执行。

  • 硬件层面,是锁住了一个北桥的信号量。

1) CPMXCHG

  • 用于比较并交换操作数,CPU对CAS的原语支持
  • 非原子性,最早用于单核CPU

2) LOCK前缀

  • CPU保证被其修饰的指令的原子性

  • 禁止重排序

  • 缓存刷新到内存

借助什么来实现?

1. 对象头中的Mark Word

偏向锁

if (锁的标记位 == 01) {
    if (偏向标记是1){
        是偏向锁且可偏向
        boolean CAS操作结果 = CAS操作替换偏向线程的ID为当前线程
        if ( CAS操作结果 == 成功){
            当前线程获得锁
            执行同步代码块
        } else {
            CAS操作失败
            开始【偏向锁的撤销】{
                等到全局安全点
                var 状态 = 检查原来持有锁的线程的状态
                if (状态 == terminated || 已经退出同步代码区)
                    原线程释放锁
                    当前线程获得锁
                else if (状态 == runnable && 未退出同步代码区){
                    执行【偏向锁膨胀到轻量级锁】的过程{
                        原持有锁的线程栈幁分配锁记录、替换MarkWord并指向对象地址、执行同步代码块、CAS操作释放锁
                        当前线程执行轻量级锁的抢锁过程{
                            CAS自旋
                            if (自旋一定次数还没有获取锁){
                                膨胀到重量级锁
                            }
                        }
                    }
                }
            }
        }
    }else {
        goto line 4 执行CAS操作
    }
}else {
    不是偏向锁
}
复制代码

轻量级锁

if (锁的标记位 == 00) {
    是轻量级锁
    执行轻量级锁的抢占{
        当前线程的栈幁中 分配 【锁记录】,【锁记录】由两个部分构成,【displaced Markword】 和 【onwer指针】
        把锁对象的【对象头】中的【Markword】拷贝到锁记录中的【displaced Markword】中
        onwer指针 指向 该锁对象
        CAS修改锁对象的对象头,使其中的【指向线程锁记录的指针】 这一字段指向当前线程
        if (CAS操作成功){
            当前线程持有锁
        }else{
            CAS操作失败
            CAS自旋
            if (自旋超过一定次数还没有成功){
                升级为重量级锁{
                    改变Markword
                    挂起当前线程
                }
            }
        }
    }
    }else {
    不是轻量级锁
}
复制代码

2. Monitor监视器对象

Java中,每个对象里面隐式的存在一个叫monitor(对象监视器)的对象,Monitor监视器对象存在于Java对象的对象头Mark Word中,这个对象源码是采用C++实现的

 class ObjectMonitor {
...
  ObjectMonitor() {
    _header       = NULL; //markOop object header
    _count        = 0;    
    _waiters      = 0,   //Number of waiting threads
    _recursions   = 0;   //Thread reentry times
    _object       = NULL;  //Store Monitor object
    _owner        = NULL;  //Thread to get ObjectMonitor object
    _WaitSet      = NULL;  //List of threads in wait state
    _WaitSetLock  = 0 ; 
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;	// One way list
    FreeNext      = NULL ;
    _EntryList    = NULL ; //Thread waiting for lock BLOCKED
    _SpinFreq     = 0 ;   
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ; 
    _previous_owner_tid = 0; //ID of the previous owning thread of the monitor
  }
...
复制代码

流程

感谢:Jacksgong 

锁重入

  • 偏向锁的锁重入

    • 简单判断mark word中的偏向线程ID
  • 轻量级锁的锁重入

    • 再次放入锁记录, 只不过displaed markword为null

    • 通过锁记录的个数判断重入的次数

    • 在高位的是第一次的锁记录

  • 重量级锁的锁重入

    • 操作系统来实现

原文链接:https://juejin.cn/post/7049292731109081119

如果你觉的本文对你有帮助,麻烦点赞关注支持一下