synchronized 原理

原理

synchronized 的秘密其实都在同步对象上。这个对象就是一个看门人,每次只允许一个线程进来,进门后此线程可以做任何自己想做的事情,然后再出来。此时看门人会吼一嗓子:没人了,可以进来啦!其它线程听到吼声,马上都冲了过来。但总有个敏捷值最高的线程先冲入门内,那么其它线程只好继续等待。
我们一直说的同步对象,其实就是任何一个普通的对象。那么一个普通的java对象是如何来做同步这件事的呢?这是因为每个对象都关联了一个 monitor lock。
当一个线程获取了 monitor lock 后,其它线程如果运行到获取同一个 monitor 的时候就会被 block 住。当这个线程执行完同步代码,则会释放 monitor lock。在后一个线程获取锁后,happens-before 原则生效,前一个线程所做的任何修改都会被这个线程看到。
再深入底层一点来分析。每个 Java 对象在 JVM 的对等对象的头中保存锁状态,指向 ObjectMonitor。ObjectMonitor 保存了当前持有锁的线程引用,EntryList 中保存目前等待获取锁的线程,WaitSet 保存 wait 的线程。此外还有一个计数器,每当线程获得 monitor 锁,计数器 +1,当线程重入此锁时,计数器还会 +1。当计数器不为0时,其它尝试获取 monitor 锁的线程将会被保存到EntryList中,并被阻塞。当持有锁的线程释放了monitor 锁后,计数器 -1。当计数器归位为 0 时,所有 EntryList 中的线程会尝试去获取锁,但只会有一个线程会成功,没有成功的线程仍旧保存在 EntryList 中。由此可以看出 monitor 锁是非公平锁。

synchronized 使用注意

  1. synchronized 使用的为非公平锁,如果你需要公平锁,那么不要使用 synchronized。可以使用 ReentrantLock,设置为公平锁。关于 ReentrantLock,会在后面章节进行讲解;
  2. 对象不能为 null。如果锁对象为 null,何谈对象头,以及保存与其关联的 monitor 锁呢?所以代码中要确保synchronized使用的锁对象不为 null;
  3. 只把需要同步的代码放入 synchronized 代码块。如果不思考,为了线程安全把方法中全部代码都放入同步代码块,那么将会丧失多线程的优势。再多的线程也只能串行执行,这完全违背了并发的初衷;
  4. 只有使用同一个对象作为锁对象,才能同步。记住是同一个对象,而不是同一个类。有一种常犯的错误是,不同线程持有的是同一个类的不同实例。那么该对象实例用作锁对象的话,多个线程并不会同步。还一种错误是使用不同类的实例作为锁对象,但是期望不同位置的同步代码块能够同步执行。这是不可能达到你想要的效果的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值