Synchronized实现原理及和Lock的区别

Synchronized

无锁,偏向锁,轻量级锁 ,重量级锁

偏向锁:对象头存储线程ID,可重入(根据线程ID判断)指的就是,把markword的线程ID改为自己线程ID的过程 偏向锁不可重偏向 批量偏向 批量撤销

轻量级锁:复制对象头到Lock Record 记录锁信息,拥有锁 复制的Lock Rrecord 指向对象头,自旋获取锁(撤销偏向锁,升级轻量级锁 线程在自己的线程栈生成LockRecord ,用CAS操作将markword设置为指向自己这个线程的LR的指针,设置成功者得到锁

重量级锁:moniter监控 ,阻塞,竞争加剧:有线程超过10次自旋, -XX:PreBlockSpin, 或者自旋线程数超过CPU核数的一半, 1.6之后,加入自适应自旋 Adapative Self Spinning , JVM自己控制 升级重量级锁:-> 向操作系统申请资源,linux mutex , CPU从3级-0级系统调用,线程挂起,进入等待队列,等待操作系统的调度,然后再映射回用户空间

 

 

MarkWork

synchronized优化的过程和markword息息相关

用markword中最低的三位代表锁状态 其中1位是偏向锁位 两位是普通锁位

(以上实验环境是JDK11,打开就是偏向锁,而JDK8默认对象头是无锁)

偏向锁默认是打开的,但是有一个时延,如果要观察到偏向锁,应该设定参数

 

锁升级的过程

JDK较早的版本 OS的资源 互斥量 用户态 -> 内核态的转换 重量级 效率比较低

现代版本进行了优化

无锁 - 偏向锁 -轻量级锁(自旋锁)-重量级锁

 

偏向锁 - markword 上记录当前线程指针,下次同一个线程加锁的时候,不需要争用,只需要判断线程指针是否同一个,所以,偏向锁,偏向加锁的第一个线程 。hashCode备份在线程栈上 线程销毁,锁降级为无锁

有争用 - 锁升级为轻量级锁 - 每个线程有自己的LockRecord在自己的线程栈上,用CAS去争用markword的LR的指针,指针指向哪个线程的LR,哪个线程就拥有锁

自旋超过10次,升级为重量级锁 - 如果太多线程自旋 CPU消耗过大,不如升级为重量级锁,进入等待队列(不消耗CPU)-XX:PreBlockSpin

 

自旋锁在 JDK1.4.2 中引入,使用 -XX:+UseSpinning 来开启。JDK 6 中变为默认开启,并且引入了自适应的自旋锁(适应性自旋锁)。

自适应自旋锁意味着自旋的时间(次数)不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也是很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。如果对于某个锁,自旋很少成功获得过,那在以后尝试获取这个锁时将可能省略掉自旋过程,直接阻塞线程,避免浪费处理器资源。

 

偏向锁由于有锁撤销的过程revoke,会消耗系统资源,所以,在锁争用特别激烈的时候,用偏向锁未必效率高。还不如直接使用轻量级锁。

 

 

 

 

Synchronized不同使用方法区别:

  

 

可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。

  • 对于Java ReentrantLock而言, 他的名字就可以看出是一个可重入锁,其名字是Re entrant Lock重新进入锁。

  • 对于Synchronized而言,也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁

    synchronized void setA() throws Exception{
        Thread.sleep(1000);
        setB();
    }
    
    synchronized void setB() throws Exception{
        Thread.sleep(1000);
    }

    上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。

Synchronized和ReentrantLock区别: 

1.比Synchronized更灵活

2.lock()获取锁,unlock()释放锁,要手动在finally中调用unlock()释放锁。

3.Synchronized惊群效应

  Synchronized中的wait和notify ,而Lock是借助于Condition类实现,更灵活,可以选择性的进行线程通知,在调度线程上更加灵活。

 Synchronized惊群效应,当有一个线程获取锁时候,其他线程进入WaitSet队列,wait()之后的nofity()时线程在争夺同一把锁的时候是随机的,谁抢到就给谁。           notifyAll() 会有一种惊群效应,一旦锁被释放了,所有的wait线程被唤醒。被通知的线程是有JVM 随机选择的 ,Synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上,线程开始notiifyAll()时,需要通知所有的WAITING线程,没有选择权,会出现相当大的效率问题。

4.Lock对象可以创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以选择性的进行线程的通知,在调度线程上更加灵活。

 Lock lock=new ReentrantLock();
 Condition condition=lock.newCondition();
  condition.await();
condition.signal();
condition.signalAll();
 

Condition中的signalAll  

     /** 从Condition中移动所有的等待线程到 拥有锁队列里
         * Moves all threads from the wait queue for this condition to
         * the wait queue for the owning lock.
         * 是 queue队列而不是waitSet 集合
         * @throws IllegalMonitorStateException if {@link #isHeldExclusively}
         *         returns {@code false}
         */
        public final void signalAll() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignalAll(first);
        }

/** 
         * Removes and transfers all nodes.
         * @param first (non-null) the first node on condition queue
         */
        private void doSignalAll(Node first) {
            lastWaiter = firstWaiter = null;
            do {
                Node next = first.nextWaiter;
                first.nextWaiter = null;
                transferForSignal(first);
                first = next;
            } while (first != null);
        }

为什么会有偏向锁,轻量级锁,重量级锁:

1.为什么要引入偏向锁?

因为经过HotSpot的作者大量的研究发现,大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁。

2.为什么要引入轻量级锁?

轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋这等待锁释放

3.轻量级锁什么时候升级为重量级锁?

自旋的时间太长也不行,因为自旋是要消耗CPU的,因此自旋的次数是有限制的,比如10次或者100次,如果自旋次数到了线程1还没有释放锁,或者线程1还在执行,
线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。
重量级锁线程会被挂起park,耗性能 

Synchronized的锁升级过程

 

posted @ 2019-10-22 10:58  暖暖-木木  阅读(1914)  评论(0编辑  收藏  举报