美文网首页
004 线程锁synchronized | 重量级锁 | 轻量级

004 线程锁synchronized | 重量级锁 | 轻量级

作者: __destory__ | 来源:发表于2019-03-20 11:30 被阅读0次

    多线程情况下,容易出现资源共享问题,此类安全问题发生的条件

    • 多线程情况
    • 涉及到共享的资源
    • 对资源的操作非原子性

    如下代码,

    public class Sequence {
        private int value;
            public  int getNext() {
            return value ++;
            }
    }
    
    Sequence s = new Sequence();
    //多线程使用 s.getNext(),就会出现共享安全问题
    
    new Thread(new Runnable() {
            public void run() {
                while(true) {
                    System.out.println(Thread.currentThread().getName() + " " + s.getNext());   
                    }
                }
    }).start();
            
    new Thread(new Runnable() {
            public void run() {
                while(true) {
                    System.out.println(Thread.currentThread().getName() + " " + s.getNext());   
                    }
                }
    }).start();
    

    value ++是非原子性操作,解决此问题的方式,使用synchronized。

    内置锁synchronized

    锁的作用,在于借助被锁的对象,实现并发访问控制,这个被锁的对象,可以是共享资源对象,也可以是其他对象,比如上述例子,可以锁住value,也可以锁住Sequence,当然也可以是锁本身,比如Lock

    锁的信息放在哪里?原则上可以锁住任何对象,锁信息放在被锁对象的对象头中。

    对象头的信息包括,

    1. Mark Word
    2. Class Metadata Address
    3. Array Length

    其中,锁信息存放在Mark Word中,

    synchronized使用范围

    • 修饰对象方法,
    • 修饰类方法
    • 修饰代码块
    /**
    * synchronized 放在普通方法上,内置锁就是当前类的实例
    * @return
    */
    public synchronized int getNext() {
        return value ++;
     }
    
     /**
    * 修饰静态方法,内置锁是当前的Class字节码对象
    * Sequence.class
    * @return
      */
      public static synchronized int getPrevious() {
        return value --;
      }
    
    //作用块
    public int test() {
        synchronized (Sequence.class) { //锁对象,锁类都可以
            if(value > 0) {
                return value;
            } else {
                return -1;
            }   
        }
    }
    

    对象锁,不同对象的锁不同,类锁,不同对象的锁相同

    synchronized的字节码语义,借助javap -verbose xx.class可以看到,如下,


    synchronized字节码

    可以看到有一个monitorenter和多个monitorexit(为了防止异常等,确保一定要exit),因此,synchronized是借助monitorenter和monitorexit来实现锁的控制,内置锁在Java中被抽象为监视器锁(monitor),在JDK 1.6之前,监视器锁可以认为直接对应底层操作系统中的互斥量(mutex)。在代码块或方法声明上添加synchronized关键字即可使用内置锁。

    内置锁的自动优化

    内置锁,在同步方式的成本非常高,包括系统调用引起的内核态与用户态切换、线程阻塞造成的线程切换等。随着JVM的升级,JDK6之后,几乎不需要修改代码,就可以直接享受JVM在内置锁上的优化成果。从简单的重量级锁,到逐渐膨胀的锁分配策略,使用了多种优化手段解决隐藏在内置锁下的基本问题。

    重量级锁

    使用场景,竞争激烈

    synchronized最终的效果就是重量级锁,无论是否并发,每次遇到此关键词,都会加锁和减锁,实际情况,可能从来没有并发访问过资源,这样无意义的加锁和减锁,造成性能浪费,因此,synchronized最初就是重量级锁。

    轻量级锁

    使用场景,完全没有实际的锁竞争,计算是多线程,实际情况下,没有出现过竞争

    使用轻量级锁时,不需要申请互斥量,仅仅将Mark Word中的部分字节CAS更新指向线程栈中的Lock Record,如果更新成功,则轻量级锁获取成功,记录锁状态为轻量级锁;否则,说明已经有线程获得了轻量级锁,目前发生了锁竞争(不适合继续使用轻量级锁),接下来膨胀为重量级锁。

    Mark Word不做解释,对象头的一部分,Lock Record属于栈帧,每个线程都拥有自己的线程栈(虚拟机栈),记录线程和函数调用的基本信息

    CAS自旋锁

    CAS(Compare-and-Swap),即比较并替换,是一种实现并发算法时常用到的技术,将其用到锁中,操作如下,

    • 当前线程竞争锁失败时,打算阻塞自己
    • 不直接阻塞自己,而是自旋(空等待,比如一个空的有限for循环)一会
    • 在自旋的同时重新竞争锁
    • 如果自旋结束前获得了锁,那么锁获取成功;否则,自旋结束后阻塞自己

    可以看到,如果在自旋的时间内,资源被老的owner释放掉了,当前线程就不需要阻塞自己,而是直接获得锁,减少线程切换了。

    CAS自旋锁缺点

    • 单核处理器上,不存在实际的并行,当前线程不阻塞自己的话(让出CPU使用权),旧owner就不能执行,锁永远不会释放,此时不管自旋多久都是浪费;进而,如果线程多而处理器少,自旋也会造成不少无谓的浪费。
    • 自旋锁要占用CPU,如果是计算密集型任务,这一优化通常得不偿失,减少锁的使用是更好的选择。
    • 如果锁竞争的时间比较长,那么自旋通常不能获得锁,白白浪费了自旋占用的CPU时间。这通常发生在锁持有时间长,且竞争激烈的场景中,此时应主动禁用自旋锁

    使用-XX:-UseSpinning参数关闭自旋锁优化;-XX:PreBlockSpin参数修改默认的自旋次数

    轻量级锁,拥有者和自旋锁一样的缺点,毕竟实现方式借助了CAS

    偏向锁

    使用场景,减少无竞争且只有一个线程使用锁的情况下,使用轻量级锁产生的性能消耗

    轻量级锁每次申请、释放锁都至少需要一次CAS,但偏向锁只有初始化时需要一次CAS,注意,假定条件是一个线程,但凡发现竞争出现,则膨胀为轻量级锁。

    偏向锁假定将来只有第一个申请锁的线程会使用锁(不会有任何线程再来申请锁),因此,只需要在Mark Word中CAS记录owner(线程ID,本质上也是更新,但初始值为空),如果记录成功,则偏向锁获取成功,记录锁状态为偏向锁,以后当前线程等于owner就可以零成本的直接获得锁;否则,说明有其他线程竞争,膨胀为轻量级锁。

    重温下Mark Word中的内容,更容易锁和后续流程图

    • 线程ID
    • Epoch
    • 对象的分代年龄信息
    • 是否是偏移锁
    • 锁标志位

    偏向锁缺点

    适用于一个线程,如果发现竞争,则膨胀为轻量级锁,通过-XX:-UseBiasedLocking可以禁止适用偏向锁,默认打开。

    总结

    场景对比

    • 偏向锁:无实际竞争,且将来只有第一个申请锁的线程会使用锁。
    • 轻量级锁:无实际竞争,多个线程交替使用锁;允许短时间的锁竞争,可以自旋一会。
    • 重量级锁:有实际竞争,且锁竞争时间长。

    其他说明

    • 内置锁只能沿着偏向锁、轻量级锁、重量级锁的顺序逐渐膨胀

    配图

    来自,https://blog.dreamtobe.cn/https://www.jianshu.com/p/36eedeb3f912

    synchronized流程

    相关文章

      网友评论

          本文标题:004 线程锁synchronized | 重量级锁 | 轻量级

          本文链接:https://www.haomeiwen.com/subject/otaamqtx.html