写点什么

JUC 浅析(二)

作者:andy
  • 2022-10-28
    北京
  • 本文字数:967 字

    阅读完需:约 3 分钟

获取非公平锁

非公平锁和公平锁在获取锁的方法上,流程是一样的;它们的区别主要表现在“尝试获取锁的机制不同”。简单点说,“公平锁”在每次尝试获取锁时,都是采用公平策略,根据等待队列依次排序等待;而“非公平锁”在每次尝试获取锁时,都是采用的非公平策略,无视等待队列,直接尝试获取锁,如果锁是空闲的,即可获取状态,则获取锁。

1. lock()

lock()在 ReentrantLock.java 的 NonfairSync 类中实现,它的源码如下:

final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1);}

说明:

lock()会先通过 compareAndSet(0, 1)来判断“锁”是不是空闲状态。是的话,“当前线程”直接获取“锁”;否则的话,调用 acquire(1)获取锁。

(01)compareAndSetState()是 CAS 函数,它的作用是比较并设置当前锁的状态。若锁的状态值为 0,则设置锁的状态值为 1。

(02)setExclusiveOwnerThread(Thread.currentThread())的作用是,设置“当前线程”为“锁”的持有者。

2. acquire()

acquire()在 AQS 中实现的,它的源码如下:

public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt();}

说明:

(01)“当前线程”首先通过 tryAcquire()尝试获取锁。获取成功的话,直接返回;尝试失败的话,进入到等待队列依次排序,然后获取锁。

(02)“当前线程”尝试失败的情况下,会先通过 addWaiter(Node.EXCLUSIVE)来将“当前线程”加入到"CLH 队列(非阻塞的 FIFO 队列)"末尾。

(03)然后,调用 acquireQueued()获取锁。在 acquireQueued()中,当前线程会等待它在“CLH 队列”中前面的所有线程执行并释放锁之后,才能获取锁并返回。如果“当前线程”在休眠等待过程中被中断过,则调用 selfInterrupt()来自己产生一个中断。

“公平锁”和“非公平锁”关于 acquire()的对比

公平锁和非公平锁,两者的不同,就是尝试获取锁的机制不同,实质上就是 tryAcquire()函数的实现不同。

通过查看源码,我们可以分析出,非公平锁 tryAcquire()的作用就是尝试去获取锁。

(01) 如果“锁”没有被任何线程拥有,则通过 CAS 函数设置“锁”的状态为 acquires,同时,设置“当前线程”为锁的持有者,然后返回 true。

(02) 如果“锁”的持有者已经是当前线程,则将更新锁的状态即可。

(03) 如果不术语上面的两种情况,则认为尝试失败。

用户头像

andy

关注

还未添加个人签名 2019-11-21 加入

还未添加个人简介

评论

发布
暂无评论
JUC 浅析(二)_andy_InfoQ写作社区