博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA并发编程:干掉 Synchronized
阅读量:7002 次
发布时间:2019-06-27

本文共 2020 字,大约阅读时间需要 6 分钟。

hot3.png

Synchronized 是通过实例对象的monitor来实现的。

Synchronized 能锁住的两种方式:

  • 锁对象

    锁对象,只锁定当前对象本身,其他的实例对象,不能同时锁定 在方法上

    //锁对象方法public void synchronized sync()

    在对象上

    //锁this对象public void sync(){	synchronized(this){	}}

    上面两者是等价的,都是锁在当前对象上。只锁自己的对象

  • 锁类

    锁类上,是对当前类所有的实例对象加锁

    //锁静态方法public static void synchronized sync()
    //锁类字节public void sync(){	synchronized(Students.class){	}}

    对类字节进行锁定,则锁定的是JVM内,此类的所有对象。 这个是怎么处理的呢? 以什么为标识来锁的呢?

Synchronized 是通过monitor来实现锁定的,每一个对象都有自己的monitor,

monitor中维护了两个列表,_EntrySet和_WaitSet, 这两个是做什么的呢?结合下图来说明。

 

_EntrySet: 当 A 线程拿到 monitor锁,对 monitor 进行计数加 1,B 线程再去获取锁是获取不到的。此时 B 线程进入 _EntrySet 列表中等待。

当然,在等待的时候 B 也是不什么都不干,因为没有其他线程通知它 锁什么时候会释放,只能自己去尝试拿锁。能拿的到就OK,拿不到就继续等着。这个时候B 就是在自旋,一遍遍的去尝试获取锁,旋转跳跃永不停歇的那种。如果 B 一直在自旋,那么也会占用很多的CPU资源,也干不了什么事。在Hotspot里,这里是有一个【智能自旋】,参考:.......

_WaitSet:  当 A 线程在锁中,调用了 wait() 方法,wait方法会释放掉 monitor 锁,然后 A 线程就进入了 _WaitSet 列表中。

wait 方法 就是让出资源,让别人先走,自己停一下。自己休息够了,再去尝试获取锁资源。

那么处在 _WaitSet 中的线程,有两种方式可以醒来:

  • 被别人 notify()
  • 设置时间,自己休息够了就醒来

醒来后还是在 _WaitSet 中,但是可以去获取锁资源了。

官方描述,notify() 是从 _WaitSet中随机唤醒一个线程,其实每次都是从 Set 的第一个线程,因为Set是无序的,所以才会是随机唤醒。

那么还有个问题,已经被唤醒的线程,还在 _WaitSet 中,还会不会被 notify() 呢 ? ?

Synchronized 是怎么做到同步的 ?

Synchronized 当多个线程竞争同一个 monitor 时,加锁的动作,就是对monitor进行计数加 1 , 每有一次加锁成功,就计数加 1。

那么如何保证不会有多个线程的冲突操作,A 从0加1,B也是从0加1. 同时操作,最后结果也是1,A,B都算是成功。是怎么避免这种问题的呢?

这里就引入了CAS。最后在写入到内存中时,会先判断一下,内存中的值是不是操作前的值。是就操作,不是就再来一次。

Synchronized 是不是可重入锁 ?

是可重入锁。为什么会有重入锁? 锁套锁,里面的锁就是要重入的锁。

monitor 里维护了得到锁的线程对象。A 线程已经持有 monitor 锁,再次获取锁时,会先判断,进来的这个线程是不是已有的线程,是的话就计数再加 1,表示再一次获得了锁。

Synchronized 是如何释放锁的 ?

通过 monitorexit 指令。monitorexit 会对 锁计数进行 【减 1】操作。加锁和解锁是成对出现的,加几次锁,就要解几次锁。

wait() 方法也是会解锁的

Synchronized 为什么说它是重量锁 ?

说它是重量锁,肯定是相对而言的,相对的是Lock。 即 AQS。

这个时候就要有对比了。

  • Synchronized 获取不到锁的时候,会阻塞线程 , AQS则不会,要不要阻塞线程自己决定。(这是重点)
  • Synchronized 是底层实现的(JVM),AQS是上层实现的(JDK)。底层逻辑实现,为了保证健壮,有很多保护逻辑在里面。(我也说不上来,但就是有。C我已经看不懂了。)
  • 还有就是 Synchronized 限定死了锁的逻辑流程,
    • 没有拿到锁就要等;
    • wait了之后不能就不处理了,还得等再获取锁,再继续流程,不能断。
    • 就一定是重入锁了,想不重入都不行。
  • 就锁的逻辑二者差不大,都是通过CAS操作来实现并发锁。就以上而言,重不重看自己的需求了。

 

欢迎指正,想到哪写到哪,也没个顺序排版啥的。这个也得学着梳理下。

转载于:https://my.oschina.net/ElEGenT/blog/3015624

你可能感兴趣的文章