非公平锁
基本概念
-
非公平锁和公平锁在获取锁的方法上,流程是一样的,区别在与获取锁的机制不同
- 非公平锁在每次尝试获取锁时,采用的是非公平策略,无视等待队列,直接尝试获取锁,如果锁是空闲的,即可获取锁的状态,则获取锁
- 公平锁在每次尝试获取锁时,采用的是公平策略,根据等待队列依次排序等待获取锁
获取非公平锁
lock
- lock() 在ReentrantLock的NonfairSync类中实现:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
-
lock() 方法首先通过调用compareAndSetState(0, 1) 来判断锁是否为空闲状态
-
如果是空闲状态,当前线程可以直接获取锁
-
如果不是空闲状态,则通过调用acquire(1) 获取锁
- compareAndSet() 是CAS函数,作用是比较并设置当前锁的状态
-
-
setExclusiveOwnerThread(Thread.currentThread()) 的作用是设置当前线程为锁的持有者
-
公平锁的lock()方法和非公平锁lock()方法的比较:
- 公平锁的lock() 方法中直接调用acquire(1) 方法获取锁
- 非公平锁的lock() 方法会首先判断当前锁的状态是否为空闲状态,如果是空闲状态,直接获取锁
acquire
- acquire() 在AQS中实现:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
-
当前线程首先通过tryAcquire() 尝试获取锁,如果获取成功,直接返回. 如果获取失败,则进入到等待队列依次排序,然后获取锁
-
当前线程在尝试失败的情况下,会通过addWaiter(Node.EXCLUSIVE) 将当前线程加入到非阻塞的FIFO队列CLH队列的
-
调用acquireQueued() 方法获取锁:
- 当前线程会等待CLH队列中前面的所有线程执行并释放锁之后,才能获取锁并返回
- 如果当前线程在等待过程中被中断过,会调用selfInterrupt() 自己产生一个中断
-
公平锁的acquire()方法和非公平锁的acquire()方法的比较:
- 公平锁和非公平锁只有tryAcquire() 方法的实现不相同,也就是尝试获取锁的机制不同,即获取锁的策略不同
tryAcquire
- 非公平锁的tryAcquire() 在ReentrantLock类中的NonfairSync类中实现:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
-
公平锁的tryAcquire()方法和非公平锁的tryAcquire()方法的比较:
- 公平锁的tryAcquire() 方法尝试获取锁时,即使锁没有被任何线程所持有,也会判断当前线程是否在CLH队列的表头,只有当前线程在CLH队列的表头时,才会获取锁
- 非公平锁的tryAcquire() 方法尝试获取锁时,只要锁没有被任何线程所持有,就会直接获取锁
nonfairTryAcquire
- nonfairTryAcquire() 在ReentrantLock类的Sync类中实现:
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取锁的状态
int c = getState();
if (c == 0) {
/*
* c == 0表示锁没有被任何线程所持有
* 如果锁没有被任何线程锁持有,则通过CAS函数设置锁的状态为acquires
* 同时,设置当前线程为锁的持有者
*/
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
// 如果锁已经被当前线程锁持有,那么更新锁的状态
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
-
nonfairTryAcquire() 的作用就是尝试获取锁:
- 如果锁没有被任何线程所持有,那么就通过CAS函数设置锁的状态为acquires, 同时设置当前线程为锁的持有者,返回true
- 如果锁的持有者是当前线程,则更新锁的状态,返回true
- 如果不属于以上两种情况,则尝试获取锁失败,返回false
释放非公平锁
unlock
- unlock() 在ReentrantLock中实现:
public void unlock() {
sync.release(1);
}
-
unlock() 是释放锁函数,是通过AQS中的release() 函数实现的
-
1的含义: 锁的状态的参数
- 对于独占锁来说,当锁处于可获取状态时,状态值为0
- 对于独占锁来说,当锁初次被线程获取到时,状态值为1
-
因为公平锁是可重入锁,对于同一个线程,每次释放锁,锁的状态就会减1
release
- release() 在AQS中实现:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
- release() 中首先会调用tryRelease() 尝试释放当前线程持有的锁. 如果成功,则唤醒后继等待的线程,并且返回true. 如果失败,则返回false
tryRelease
- tryRelease() 在ReentrantLock类中的Sync类中实现:
protected final boolean tryRelease(int releases) {
// 定义释放锁之后的状态
int c = getState() - releases;
// 如果当前线程不是锁的持有者,则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
// 如果当前锁已经被当前线程释放,则设置锁的持有者为null.此时锁是可获取状态
free = true;
setExclusiveOwnerThread(null);
}
// 设置释放锁之后的当前线程的锁的状态
setState(c);
return free;
}
-
tryRelease() 的作用是尝试释放当前锁
- 如果当前线程不是锁的持有者,则抛出异常
- 在当前线程释放锁操作之后,当前线程对锁的拥有状态为0, 此时当前线程彻底释放该锁.设置锁的持有者为null, 这是锁即为可获取状态,同时更新当前线程的锁的状态为0
unparkSuccessor
- 在release() 中当前线程释放锁成功之后,会唤醒当前线程的后继线程
- 根据CLH队列的先入先出FIFO原则,当前线程必然是头结点head, 如果CLH队列非空,则唤醒下一个等待线程
private void unparkSuccessor(Node node) {
// 获取当前线程的状态
int ws = node.waitStatus;
if (ws < 0)
// 如果状态为负,则设置状态值为0
compareAndSetWaitStatus(node, ws, 0);
/*
* 获取当前节点的有效的后继节点
* 如果是无效的节点,则使用for循环从尾部开始寻找
* 这里的有效是指后继节点对应的状态的
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 唤醒后继节点对应的线程
if (s != null)
LockSupport.unpark(s.thread);
}
- unparkSuccessor() 的作用的是唤醒当前线程的后继线程
- 后继线程唤醒之后,就可以继续获取锁并恢复运行
总结
-
公平锁和非公平锁的区别在于获取锁的机制的不同:
- 公平锁在尝试获取锁时,只有当前线程在CLH等待队列的表头,才会获取锁
- 非公平所在尝试获取锁时,只要锁处于空闲状态,未被其余线程所持有,则直接获取锁,不考虑当前线程在CLH等待队列的顺序