Synchronized升级过程总结

要理解Synchronized,首先要清楚偏向锁,轻量级锁和重量级锁,在使用方面须要有wait/wait(time)/notify/notifyAll等,下面咱们就来介绍一下Synchronized的流程和使用方法;java

  • Synchronized的升级过程

    (Java SE 1.6中为了减小得到锁和释放锁带来的 性能消耗而引入的偏向锁和轻量级锁)git

    Synchronized的升级顺序是 无锁-->偏向锁-->轻量级锁-->重量级锁,顺内不可逆。github

    偏向锁

    当一个线程访问同步代码块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,偏向锁是一个可重入的锁,之后该线程在进入和退出该同步代码块时不须要花费 CAS 操做来加锁和解锁,而只需简单的测试一下对象头的 Mark Word 里是否存储着指向当前线程的偏向锁(当前线程的线程ID),若是测试成功,表示线程已经得到了锁,若是测试失败,则须要再测试下 Mark Word 中偏向锁的标识是否设置成 1(表示当前是偏向锁),若是偏向锁标识是1,则使用 CAS 进行锁获取,偏向锁标识不是1,则尝试使用 CAS 将对象头的偏向锁指向当前线程,上述两种CAS获取锁的操做,若是CAS操做成功则获取到了偏向锁失败则表明出现了锁竞争,须要锁撤销操做安全

    锁撤销

    偏向锁使用了一种等到竞争出现才释放锁的机制,因此当其余线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销须要等待拥有偏向锁的线程到达全局安全点(在这个时间点上没有字节码正在执行),会首先暂停拥有偏向锁的线程,而后检查持有偏向锁的线程是否活着,若是线程不处于活动状态,则将锁的对象的对象头设置成无锁状态,若是线程仍然活着,拥有偏向锁的栈会被执行**(判断是否须要持有锁),遍历偏向对象的锁记录,查看使用状况,若是还须要持有偏向锁,则偏向锁升级为轻量级锁**,若是不须要持有偏向锁了,则将锁对象恢复成无锁状态,最后唤醒暂停的线程。ide

    轻量级锁

    线程在执行同步块以前,JVM 会先在当前线程的栈桢中建立用于存储锁记录的空间,并将对象头中的 Mark Word 复制到锁记录中,官方称为 Displaced Mark Word。而后线程尝试使用 CAS 将对象头中的 Mark Word 替换为指向锁记录的指针。若是成功,当前线程得到锁,若是失败,表示其余线程竞争锁,当前线程便尝试使用自旋来获取锁自旋有必定次数,若是超过设置自旋的次数则升级到重量级锁或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁,重量级锁使除了拥有锁的线程之外的线程都阻塞,防止CPU空转性能

    轻量级锁解锁

    轻量级解锁时,会使用原子的 CAS 操做来将 Displaced Mark Word 替换回到对象头,若是成功,则表示没有竞争发生。若是失败,表示当前锁存在竞争,锁就会膨胀成重量级锁测试

    **例如:**T1线程持有锁,T2线程自旋,可是T2线程自旋最大次数已通过了,则自旋失败,进行锁升级到重量级锁,T2线程阻塞,这时T1执行完了同步代码块,进行轻量级锁解锁,可是这时Mark Word中的标志位已经从原来的00(偏向锁)变成了10(中练级锁),解锁会CAS失败,T1会进行解锁(释放监视器,释放锁),并唤醒线程T2.ui

    重量级锁

    Synchronized是非公平锁,Synchronized在线程进入阻塞队列时,等待的线程会先尝试获取锁,若是获取不到就进入阻塞队列,这明显对于已经进入队列的线程是不公平的。spa

    优缺点

    优势 缺点 场景
    偏向锁 加解锁不须要过多的资源消耗,和非同步方法的相比仅仅是纳秒的差距 若是存在所竞争,会有额外的锁撤销操做 适用于只有一个线程访问的场景
    轻量级锁 竞争线程不会阻塞,会自旋,减小了上线文切换。 若是始终得不到锁,会消耗cpu资源 追求响应时间,同步代码块多为计算,执行快的场景
    重量级锁 没啥优势 线程阻塞响应时间慢 同步代码块执行时间较长的场景使用

    流程图

  • Synchronized 方法

    1: Synchronized 是java的内置锁,也是排它锁非公平锁排它锁也就是当前线程获取锁后,其余线程都会阻塞挂起 ,非公平锁是在线程后去锁的时候会先尝试后去锁,获取不到在进行阻塞。线程

    2: Synchronized 是如何保证 '原子性' 的?是由于进入 Synchronized 块的内存语义是把 Synchronized 块内使用的 '工做内存清除', 这样在使用共享变量时就会直接存主内存中获取并复制到工做你内存中,在退出 Synchronized 语句块时 会把 工做内存中计算过的 '共享变量' 更新到主内存中。

    3: 获取到 Synchronized 锁 ,都是 '对象锁'而非'代码块锁' (锁的都是对象或类,而不是某个方法),所以 Synchronized 是具备可重入性,在获取到该对象锁后能够不用再次获取该对象其余方法的锁,直接进入。

    4: 若是是 Synchronized 用在 static 上, 就表明是类锁(.class)不管建立多少个对象都不可行

    区别

    wait和sleep区别在于wait会释放锁, 可是sleep不会释放锁 ,sleep会致使线程阻塞挂起。

    wait/wait(timeout)/notify/notifyAll 方法仅能够在获取到锁后才可使用。

    解释

    wait: 线程等待。

    wait(time): 线程等待,若是时间超过了设置的time,则继续执行。

    notify: 随机唤醒一个等待的线程。

    notifyAll: 唤醒所有等待线程。

    代码演示

    /** * @Auther: concurrenncy * @Date: 2019-03-25 16:43 * @Company: 随行付支付有限公司 * @maill: lan_tao@suixingpay.com * @Description: wait 和 sleep 区别在于 wait会释放锁, 可是 sleep 不会 ,sleep会致使线程阻塞挂起 */
            public class WaitAndNotifyTest {
    
                private static Object obj = new Object();
    
                public static void main(String[] args) {
                    // 建立线程 thread1
                    Thread thread1 = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                System.out.println(Thread.currentThread().getName() + " begin wait...");
                                synchronized (obj) {
                                    obj.wait();
                                }
                                System.out.println(Thread.currentThread().getName() + " end wait...");
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }, "thread1");
    
                    // 建立线程 thread2
                    Thread thread2 = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                System.out.println(Thread.currentThread().getName() + " begin wait...");
                                synchronized (obj) {
                                    obj.wait();
                                }
                                System.out.println(Thread.currentThread().getName() + " end wait...");
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }, "thread2");
    
    
    
                    // 启动
                    thread1.start();
                    thread2.start();
    
                    try {
                        // 睡眠一秒
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    // 若是调用 notify 的线程未获取 对象锁,在调用 notify 的时候会抛出 java.lang.IllegalMonitorStateException 异常
                    synchronized (obj) {
                        // 唤醒 使用 obj 调用 wait 方法的其中一个线程 (随机)
                        obj.notify();
                        // 唤醒 使用呢 obj 调用 wait 方法的全部线程
                        obj.notifyAll();
                    }
                }
            }
    复制代码

博客地址:lantaoblog.site

相关文章
相关标签/搜索