首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈synchronized与Object.wait/notify原理

浅谈synchronized与Object.wait/notify原理

作者头像
luoxn28
发布2021-04-08 14:20:49
6260
发布2021-04-08 14:20:49
举报
文章被收录于专栏:TopCoderTopCoder

synchronized是Java中常用的锁机制,synchronized+Object.wait/notify是常用的等待唤醒机制,那它们的实现原理是什么呢?本文就synchronized与Object.wait/notify为例谈谈以下内容。

  • synchronized锁升级。
  • synchronized是如何工作的。
  • synchronized同步队列和等待队列是如何配合的。
  • 为什么Object.wait必须在synchronized中。

synchronized在JVM里的实现是基于进入和退出Monitor对象来实现方法同步和代码块同步的。monitor enter指令是在编译后插入到同步代码块的开始位置,而monitor exit是插入到方法结束处和异常处,JVM要保证每个monitor enter必须有对应的monitor exit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。

Java 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,在Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态偏向锁状态轻量级锁状态重量级锁状态,这几个状态会随着竞争情况逐渐升级。 注意:synchronized可以修饰某个方法,也可以修饰某个对象。

执行monitor enter指令如果获取锁不成功,会将该线程添加到该monitor对象对应的同步队列 SynchronizedQueue,当占用该monitor对象的线程执行monitor out时就会唤醒同步队列中的线程。

那么问题来了,唤醒线程时是选择同步队列上哪一个线程呢?java8默认选择最近添加到同步队列中的那个线程。可以使用如下代码验证:

private static Object obj = new Object();

public static void main(String[] args) throws Exception {
    synchronized (obj) {
        newThread("t1");
        newThread("t2");
        newThread("t3");
        newThread("t4");
        newThread("t5");

        Thread.sleep(1000);
        System.out.println("main release");
    }

    System.out.println("main end");
    Thread.sleep(2000);
}

private static void newThread(String name) throws Exception {
    new Thread(() -> {
        synchronized (obj) {
            System.out.println(Thread.currentThread().getName() + " get");
        }
    }, name).start();
    Thread.sleep(100L);
}

输出结果为:

同步队列和等待队列

知道了synchronized基本概念之后,一起来看下Object.wait()/notify(),Monitor对象 中包含一个同步队列(由 _cxq_EntryList 组成)和一个等待队列( _WaitSet )。

  • 被notify或 notifyAll 唤醒时根据 policy 策略选择加入的队列(policy 默认为 0)
  • 退出同步块时根据 QMode 策略来唤醒下一个线程(QMode 默认为 0)

注意:synchronized的同步队列和等待队列 与 基于AQS(lock/condition)的同步队列和等待队列实现原理类似,只不过前者是一个同步队列对应一个等待队列,而后者是一个同步队列可以对应多个等待队列。

synchronized的同步队列和等待队列流程图如下所示:

到这里为止,我们大致了解了synchronized是如何工作的,以及它的同步队列和等待队列是如何配合的。

最后我们思考2个问题:

1、为什么要有Object.wait/notify,只使用synchronized不能也能实现线程阻塞然后被其他线程唤醒(通知)么

2、执行Object.wait/notify,为什么必须要在synchronized中呢

这里小伙伴们稍微休息片刻,思考下上述2个问题的原因。

关于第一个问题,只使用synchronized是为了简单的加解锁,而使用Object.wait/notify 是为了实现被通知后,再执行下一步动作的逻辑。如果Object.wait后没有任何逻辑的话,这里的Object.wait是没有意义的,可以直接去掉。

对于第二个问题,为什么必须要在synchronized中呢,我们都知道synchronized锁机制大都是为了保证原子性,因此我们有理由相信,这里的原因大概率就是原子性。那么是为了什么的原子性呢?是为了保证在Object.wait时,将当前线程添加到_WaitSet等待队列和monitor out唤醒其他线程的原子性;为了保证在Object.notify 时,在将当前线程从WaitSet移到同步队列时,锁相关的线程状态不会发生变化。

推荐阅读

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-03-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 TopCoder 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 同步队列和等待队列
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档