Synchronized

是jvm的内置隐式锁。

ReentrantLock 是显示锁

synchronized在java <1.6版本的时候,性能很低,依赖java对象锁。

synchronized的应用场景

1、修饰实例方法

2、修饰静态方法

3、修饰代码块

应用场景示例:

1、修饰实例方法

正常场景

异常场景

结论:synchronized修饰实例方法时,如果实例对象不同,结果会错误;只有相同的实例才能得出正确结果

2、修饰静态方法

结论:synchronized修饰静态方法,不论什么情况,都会加锁,保证结果正常

3、修饰代码块

正常场景

 异常场景

结论:synchronize修饰代码块时,要看后面跟的是实例对象还是类对象,有针对的去看结果。如果是类对象,则当第一个线程执行时会加锁,其他线程无法执行,从而保证结果正确;如果是实例对象,只有对象实例相同,才能保证结果正确。

Java底层数据结构支撑

理解对象头和Monitor

一个java对象包括三部分:对象头、实例变量、填充数据

对象头是实现synchronize锁的基础;对象头中包含锁的信息。

jvm中采用2个字来存储对象头(如果对象是数组则会分配3个字,多出来的1个字记录的是数组长度),其主要结构是由Mark Word 和 Class Metadata Address 组成,其结构说明如下表:

Mark Word除了上面结构外,还有如下额外结构

重点说下重量级锁,锁标识位为10,其中指针指向的是monitor对象(也称为管程或监视器锁)的起始地址。每个对象都存在着一个monitor对象,monitor的结构如下:

ObjectMonitor() {

    _header       = NULL;

    _count        = 0; //记录个数

    _waiters      = 0,

    _recursions   = 0;

    _object       = NULL;

    _owner        = NULL;

    _WaitSet      = NULL; //处于wait状态的线程,会被加入到_WaitSet

    _WaitSetLock  = 0 ;

    _Responsible  = NULL ;

    _succ         = NULL ;

    _cxq          = NULL ;

    FreeNext      = NULL ;

    _EntryList    = NULL ; //处于等待锁block状态的线程,会被加入到该列表

    _SpinFreq     = 0 ;

    _SpinClock    = 0 ;

    OwnerIsThread = 0 ;

  }

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

synchronize底层原理

修饰代码块时,编译为汇编指令时,会有monitorenter 和 monitorexit 两条指令,在monitorenter时,会尝试获取锁对象的monitor,当monitor的进入计数器为 0,那么可以获取monitor,并将计数器值设置为 1,owner设置为当前线程,取锁成功,如果当前线程再次执行到monitorenter,同样可以取锁成功,计数器加1.当其他线程尝试获取锁的时候,发现monitor里的计数器不为0,那么当前线程将被阻塞,直到正在执行线程执行完毕。

修饰方法时,方法级的同步是隐式,即无需通过字节码指令来控制的,它实现在方法调用和返回操作之中。JVM可以从方法常量池中的方法表结构(method_info Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor(虚拟机规范中用的是管程一词), 然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放monitor。在方法执行期间,执行线程持有了monitor,其他任何线程都无法再获得同一个monitor。

锁的升级(膨胀)

额外补充

锁的粗化

public synchronized  void increase() {
    System.out.println("1");
}
public synchronized  void increase2() {
    System.out.println("2");
}
public synchronized  void increase3() {
    System.out.println("3");
}

上面代码等价于

SynchronizedTest lock = new SynchronizedTest();
public  void test() {
    synchronized(lock){
        System.out.println("1");
        System.out.println("2");
        System.out.println("3");
    }
}

锁的逃逸分析

public  void test() {
    Object lock = new Object();
    synchronized(lock){
        System.out.println("1");
        System.out.println("2");
        System.out.println("3");
    }
}

这里的synchronized是无效的,因为对于test()方法执行的时候,会在线程栈内新建个lock,等价于没有。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值