【Java】synchronized 和 ReentrantLock 之间的区别

synchronized 和 ReentrantLock 之间的区别

synchronized特性

对于 synchronized 来说,它本身就是:

  1. 乐观锁 或 悲观锁(自适应)
  2. 轻量级锁 或 重量级锁(自适应)
  3. 自旋锁 或 挂起等待锁(自适应)
  4. 不是读写锁
  5. 非公平锁
  6. 可重入锁

synchronized 的加锁过程,尤其是搞清楚 “自适应” 是怎么回事:

  1. 偏向锁阶段

    • 核心思想:能不加锁就不加,但是一旦有其他线程来争这个锁,则立马对该资源加锁。

    也就是当只有一个线程在调用这个带有锁的资源的时候,synchronized 只是对该锁作一个标记,并没有真的锁上;一旦有其他线程来 “抢” 这个锁,它就会在这之前把锁获取并持有,此时就从偏向锁升级到轻量级锁。

  2. 轻量级锁阶段

    • 假设有锁竞争,但是竞争不激烈的情况下:
    • 此处就实现了 “自旋锁
      • 优势在于:其他线程释放锁之后,可以在第一时间获取并持有锁;不会有调度的开销。
      • 劣势在于:因为没有受到调度,所以一直在消耗CPU的资源
    • 于此同时,synchronized 内部也会统计当前这个锁对象上有多少个线程在参与竞争,当发现参与竞争的线程过多时,就会进一步升级成 “重量级锁”。
  3. 重量级锁阶段

    • 此时拿不到锁的线程就不会 “自旋”,而是进入 “阻塞等待” 状态,会被调度出CPU。
    • 当锁被释放时,就由系统随机唤醒一个线程来获取锁。

注:锁阶段只能 “升级”,而不能 “降级”,严格来说自适应这一说法是不太严谨的。


synchronized 的优化策略:

  1. 锁消除:当编译器在编译代码时,如果发现该代码不需要加锁,就会自动将这个锁去掉。
  2. 锁粗化:会把多个 “细粒度” 的锁,合并成一个 “粗粒度” 的锁。

粗细:一般认为 synchronized() {......} 花括号里面包含的代码越少,锁的粒度越细;如果包含的代码越多,锁的粒度就越粗。


ReentrantLock

ReentrantLocksynchronized 大致是相同的,但具备着一些 synchronized 没有的功能:

  1. ReentrantLock 提供了 tryLock() 操作

    • synchronized 的加锁是直接进行加锁,如果加锁不成功,则让线程进入阻塞等待。
    • tryLock() 是尝试进行加锁,如果加锁不成功,不会让线程进入阻塞,而是直接返回 false
  2. ReentrantLock 提供了公平锁的实现

    • synchronized 是非公平锁。
    • ReentrantLock 可以通过队列来记录加锁线程的先后顺序,实现公平锁
  3. 对等待通知机制的优化:

    • 对于 synchronized 来说,搭配了 wait()notify() 方法。如果多个线程使用同一个锁的 wait() 方法,那么 在唤醒时,是 “随机” 唤醒一个线程。
    • 而对于 ReentrantLock 来说,搭配 Condition 类,就可以实现 “指定” 唤醒某个线程。
  • 19
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

行舟Yi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值