目录
tryLock(long time, TimeUnit unit)方法
Reentrantlock介绍
Reentrantlock是一个JDK实现的支持可重入、支持超时获取锁、支持公平和非公平、支持可中断的以及支持多条件的锁。相比于Synchronized它更加灵活。由用户自己选择加锁和释放锁时机。而且Reentrantlock可以支持多个条件变量(Condition),而synchronized只支持一个条件变量。
使用方法如下:
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();
try {
//执行代码
} finally {
reentrantLock.unlock();
}
支持可重入
可重入是什么意思呢?可以这样理解:
Object lock = new Object();
synchronized (lock) {
synchronized (lock) {
synchronized (lock) {
}
}
}
reentrantLock.lock();
try {
reentrantLock.lock();
try {
reentrantLock.lock();
try {
//执行代码
} finally {
reentrantLock.unlock();
}
} finally {
reentrantLock.unlock();
}
} finally {
reentrantLock.unlock();
}
synchronized 和 reentrantLock都是可重入锁。
支持超时
tryLock(long time, TimeUnit unit)
支持公平和非公平
ReentrantLock(boolean fair)
支持可中断
lockInterruptibly()
支持多条件
在synchronized中,锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外地添加一个锁,而ReentrantLock则无须这样做,只需要多次调用newCondition()方法即可。
/***
* ReentrantLock锁可以绑定多个条件
*/
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
public class TestReentrantLock4{
static ReentrantLock lock = new ReentrantLock();
//创建两个回家条件:需要钱、需要票
static Condition moneyCondition=lock.newCondition();
static Condition ticketCondition=lock.newCondition();
Boolean havaMoney=false;//是否有钱
Boolean haveTicket=false;//是否有票
public static void main (string[] args)throws InterruptedException {
//线程farmer1
new Thread (()->
log.dubug("--------第一个农民想回家--------");
while(!havaMoney){
try {
lock.lock();
log.dubug("---------第一个农民没有钱,回不去家了,等钱---------");
moneyCondition.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
},name:"farmer1").start();
//线程farmer2
new Thread (()->
log.dubug("--------第二个农民想回家--------");
while(!haveTicket){
try {
lock.lock();
log.dubug("---------第二个农民没有票,回不去家了,等票---------");
ticketCondition.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
},name:"farmer2").start();
//主线程12306睡一会
Thread.sleep(millis:1000);
try{
lock.lock();
havaMoney=true;
log.dubug("---------第一个农民有钱了,唤醒他可以回家了---------");
moneyCondition.signal();//唤醒等钱的farm1线程执行回家。
haveTicket=true;
log.dubug("---------第二个农民有票了,唤醒他可以回家了---------");
ticketCondition.signal();//唤醒等票的farm2线程执行回家。
}catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
},name:"12306").start();
}
}
Lock
先看类结构ReentrantLock实现了Lock接口
核心代码如下
//package java.util.concurrent.locks.Lock
public interface Lock {
// 获取锁
void lock();
// 获取锁(可中断)
void lockInterruptibly() throws InterruptedException;
// 尝试获取锁,如果没获取到锁,就返回false
boolean tryLock();
// 尝试获取锁,如果没获取到锁,就等待一段时间,这段时间内还没获取到锁就返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 释放锁
void unlock();
// 条件锁
Condition newCondition();
}
lock和unlock都调用了sync,说明核心逻辑实现依靠sync类。
//package java.util.concurrent.locks.ReentrantLock
public class ReentrantLock implements Lock, java.io.Serializable {
public ReentrantLock() {
sync = new NonfairSync();
}
//sync 决定是公平还是非公平
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
public void lock() {
sync.lock();
}
public void unlock() {
sync.release(1);
}
//省略
}
ReentrantLock有公平锁和非公平锁2种模式。
AQS
在初始化时默认为NonfairSync,利用IDEA的UML关系发现sync抽象类继承自AQS,也说明了NonfairSync和FairSync都是AQS的子类。
我们先看AQS,AQS继承了AbstractOwnableSynchronizer,AbstractOwnableSynchronizer作用是设置当前拥有独占访问权限的线程,在后面会有用到。
我们可以很容易发现
(1)抽象类Sync实现了AQS的部分方法;
(2)NonfairSync实现了Sync,主要用于非公平锁的获取;
(3)FairSync实现了Sync,主要用于公平锁的获取。
AQS有两种模式,一种是独占模式,一种是共享模式,像ReentrantLock就是独占模式,因为一次只有一个线程可以竞争到锁,而像Semaphore就是共享模式,一次可以多个线程获取到资源。
阅读以下代码,在AQS中head和tail为队列的实现,state用于加锁状态变量。
unsafe.objectFieldOffset用于获取这些字段的内存地址,
//package java.util.concurrent.locks
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer{
//省略
/**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
* CANCELLED.
*/
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
private transient volatile Node tail;
/**
* The synchronization state.
*/
private volatile int state;
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
}
AbstractOwnableSynchronizer类里面实现比较简单,get和set一个线程类。
//package java.util.concurrent.locks
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}
上面说过ReentrantLock继承AQS实现了互斥模式,这里就用到了2个API:tryAcquire和tryRelease
//package java.util.concurrent.locks.AbstractQueuedSynchronizer
//互斥模式下使用:尝试获取锁
protected boolean tryAcquire(int arg) {
throw new UnsupportedoperationException();
}
//互斥模式下使用:尝试释放锁
protected boolean tryRelease(int arg) {
throw new UnsupportedoperationException();
}
//共享模式下使用:尝试获取锁
protected int tryAcquireShared(int arg) {
throw new UnsupportedoperationException();
}
//共享模式下使用:尝试释放锁
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedoperationException();
//如果当前线程独占着锁,返回true
}
protected boolean isHeldExclusive1y() {
throw new UnsupportedoperationException();
}
总结:
AQS中维护了一个队列,这个队列使用双链表实现,用于保存等待锁排队的线程;
AQS中维护了一个状态变量,控制这个状态变量(+1就是加锁一次,为0就可以解锁了)就可以实现加锁解锁操作了。
Sync
public class ReentrantLock implements Lock, java.io.Serializable {
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
}
Sync继承了AQS,这里有一个非公平锁的tryAcquire的实现。
abstract static class Sync extends AbstractQueuedSynchronizer {
abstract void lock();
final boolean nonfairTryAcquire(int acquires) {}
protected final boolean tryRelease(int releases) {}
protected final boolean isHeldExclusively() {}
final ConditionObject newCondition() {}
final Thread getOwner()
final int getHoldCount()
final boolean isLocked()
private void readObject(java.io.ObjectInputStream s)
}
NonfairSync
acquire方法是关键代码。
- 第一个#tryAcquire():该方法是尝试加锁,在AbstractQueuedSynchronizer中没有实现,由子类自己实现,采用模板方法模式。
- 第二个#addWaiter():这个方法就是将线程加入等待队列的。
- 第三个#acquireQueued():这个方法就是判断当前线程是否需要阻塞,如果不需要就一直空转,如果需要就阻塞。
tryAcquire的流程
/**
* Sync object for fair locks
*/
static final class NonFairSync extends Sync {
final void lock() {
//先CAS尝试一次获取锁(0说明没有任何锁)
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//该方法在AQS中,这里因为继承关系我写在了这里
//获取锁,arg默认都是传1,可以理解为锁一旦重入一次state就+1
public final void acquire(int arg) {
//尝试获取锁
if (!tryAcquire(arg)
//tryAcquire返回false,尝试失败,添加进入队列获取锁
&& acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//acquireQueued返回true,则说明需要中断
selfInterrupt();
//获取锁成功
//tryAcquire返回true,
//或者tryAcquire返回false但acquireQueued返回false
}
//尝试获取锁,tryAcquire返回成功则加锁成功acquire无需继续执行
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
//非公平锁的实现
//package java.util.concurrent.locks.ReentrantLock:static class Sync
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//先获取AQS里面的state
int c = getState();
if (c == 0) {
//为0说明锁没被占有,直接来一次CAS
if (compareAndSetState(0, acquires)) {
//CAS修改成功则代表加锁成功,先设置当前线程为独占模式
setExclusiveOwnerThread(current);
//成功获取锁返回
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
// 如果当前线程本身就占有着锁,现在又尝试获取锁
// 那么,直接让它获取锁并返回true
//ReentrantLock实现可重入的核心代码
int nextc = c + acquires;
//int溢出,这种情况一般很少发生,边界考虑,可以看出源码设计者的周到
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//将当前线程持有的锁+1
setState(nextc);
return true;
}
//state不为0且当前线程并不独占则直接返回尝试加锁失败,在上层继续往下执行逻辑
//acquireQueued方法
return false;
}
//封装对stateOffset使用CAS
//package java.util.concurrent.locks.AbstractQueuedSynchronizer
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
//package java.util.concurrent.locks.AbstractQueuedSynchronizer
protected final void setState(int newState) {
state = newState;
}
addWaiter的流程
先介绍AQS中Node的结构
//package java.util.concurrent.locks.AbstractQueuedSynchronizer
static final class Node {
//省略
//等待状态,有如下几种
volatile int waitStatus;
//表示线程已取消
static final int CANCELLED = 1;
//标识后继节点需要唤醒
static final int SIGNAL = -1;
//标识线程等待在一个条件上
static final int CONDITION = -2;
//标识后面的共享锁需要无条件的传播(共享锁需要连续唤醒读的线程)
static final int PROPAGATE = -3;
//双向链表
volatile Node prev;
volatile Node next;
//当前节点是哪个线程
volatile Thread thread;
// 下一个等待在条件上的节点(Condition锁时使用)
Node nextWaiter;
/** 用于指示节点正在独占模式下等待的标记 **/
static final Node EXCLUSIVE = null;
//省略
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
// 把共享模式还是互斥模式存储到nextWaiter这个字段里面了
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
// 等待的状态,在Condition中使用
this.waitStatus = waitStatus;
this.thread = thread;
}
}
tryAcquire尝试获取锁失败以后接着往下执行,队列的实现采用链表方式。在AQS的队尾add一个Node,填入独占标记和线程。
//package java.util.concurrent.locks.AbstractQueuedSynchronizer
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
//先执行一次CAS
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//执行失败了再进行循环
enq(node);
return node;
}
//死循环插入队尾,直到成功为止。队尾为null时需要给队头 队尾做初始化
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
acquireQueued流程
调用上面的addWaiter()方法使得新节点已经成功入队了,这个方法是尝试让当前节点来获取锁的
从p == head && tryAcquire(arg)可以看出,在非公平锁中,非队头节点不能进行tryAcquire,这就
说明了在非公平锁中,只有队头的线程和刚进入Lock调用tryAcquire的线程能够有资格获取该锁,这对于非队头线程来说,他们都是“公平”的,换句话说,对于所有线程来说,非公平锁不完全是非公平的。
//package java.util.concurrent.locks.AbstractQueuedSynchronizer
final boolean acquireQueued(final Node node, int arg) {
// 失败标记
boolean failed = true;
try {
// 中断标记
boolean interrupted = false;
// 自旋
for (;;) {
// 当前节点的前一个节点
final Node p = node.predecessor();
// 如果当前节点的前一个节点为head节点,则说明轮到自己获取锁了
// 调用ReentrantLock.FairSync.tryAcquire()方法再次尝试获取锁
if (p == head && tryAcquire(arg)) {
// 尝试获取锁成功,所以
// 这里同时只会有一个线程在执行,所以不需要用CAS更新
// 把当前节点设置为新的头节点
setHead(node);
// 并把上一个节点从链表中删除
p.next = null; // help GC
// 未失败
failed = false;
return interrupted;
}
// 是否需要阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
// 真正阻塞的方法
parkAndCheckInterrupt())
// 如果中断了
interrupted = true;
}
} finally {
// 如果失败了
if (failed)
// 取消获取锁
cancelAcquire(node);
}
}
// AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire()
// 这个方法是在上面的for()循环里面调用的
// 第一次调用会把前一个节点的等待状态设置为SIGNAL,并返回false
// 第二次调用才会返回true
shouldParkAfterFailedAcquire这个方法是在上面的for()循环里面调用的,第一次调用会把前一个节点的等待状态(默认为0)设置为SIGNAL,并返回false。第二次调用才会返回true
package java.util.concurrent.locks.AbstractQueuedSynchronizer
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 上一个节点的等待状态
// 注意Node的waitStatus字段我们在上面创建Node的时候并没有指定
// 也就是说使用的是默认值0
// 这里把各种等待状态再贴出来
//static final int CANCELLED = 1;
//static final int SIGNAL = -1;
//static final int CONDITION = -2;
//static final int PROPAGATE = -3;
int ws = pred.waitStatus;
// 如果等待状态为SIGNAL(等待唤醒),直接返回true
if (ws == Node.SIGNAL)
return true;
// 如果前一个节点的状态大于0,也就是已取消状态
if (ws > 0) {
// 把前面所有取消状态的节点都从链表中删除
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 如果前一个节点的状态小于等于0,则把其状态设置为等待唤醒
// 这里可以简单地理解为把初始状态0设置为SIGNAL
// CONDITION是条件锁的时候使用的
// PROPAGATE是共享锁使用的
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
// AbstractQueuedSynchronizer.parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
// 阻塞当前线程
// 底层调用的是Unsafe的park()方法
LockSupport.park(this);
// 返回是否已中断
return Thread.interrupted();
}
FairSync
acquire方法与NonFairSync一样,调用AQS里面的acquire
package java.util.concurrent.locks.AbstractQueuedSynchronizer
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
不同点体现在2个地方。
不同点1
在锁资源空闲时非公平锁情况下刚进入的线程有资格直接与队列当中的线程竞争锁资源。
FairSync:
final void lock() {
acquire(1);
}
NonfairSync:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
不同点2
在锁资源空闲时公平锁还需要判断当前是否为队头节点才能获取锁。hasQueuedPredecessors()用来判断是否有前驱结点。
FairSync:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
NonfairSync:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
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;
}
总的来说,相对于公平锁,非公平锁在一开始就多了两次直接尝试获取锁的过程。
流程总结
lockInterruptibly()方法
支持线程中断,它与lock()方法的主要区别在于lockInterruptibly()获取锁的时候如果线程中断了,会抛出一个异常,而lock()不会管线程是否中断都会一直尝试获取锁,获取锁之后把自己标记为已中断,继续执行自己的逻辑,后面也会正常释放锁。
tryLock()方法
尝试获取一次锁,成功了就返回true,没成功就返回false,不会继续尝试。这里公平锁和非公平锁都一样。
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
tryLock(long time, TimeUnit unit)方法
尝试获取锁,并等待一段时间,如果在这段时间内都没有获取到锁,就返回false。
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
这里的 tryAcquire 需要看是公平锁还是非公平锁
重点是以下方法:
// AbstractQueuedSynchronizer.doAcquireNanos()
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
// 如果时间已经到期了,直接返回false
if (nanosTimeout <= 0L)
return false;
// 到期时间
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime();
// 如果到期了,就直接返回false
if (nanosTimeout <= 0L)
return false;
// spinForTimeoutThreshold = 1000L;
// 只有到期时间大于1000纳秒,才阻塞
// 小于等于1000纳秒,直接自旋解决就得了
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
// 阻塞一段时间
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
unlock()方法
释放锁。
// java.util.concurrent.locks.ReentrantLock.unlock()
public void unlock() {
sync.release(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.release
public final boolean release(int arg) {
// 调用AQS实现类的tryRelease()方法释放锁
if (tryRelease(arg)) {
Node h = head;
// 如果头节点不为空,且等待状态不是0,就唤醒下一个节点
// 还记得waitStatus吗?
// 在每个节点阻塞之前会把其上一个节点的等待状态设为SIGNAL(-1)
// 所以,SIGNAL的准确理解应该是唤醒下一个等待的线程
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// java.util.concurrent.locks.ReentrantLock.Sync.tryRelease
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 如果当前线程不是占有着锁的线程,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果状态变量的值为0了,说明完全释放了锁
// 这也就是为什么重入锁调用了多少次lock()就要调用多少次unlock()的原因
// 如果不这样做,会导致锁不会完全释放,别的线程永远无法获取到锁
if (c == 0) {
free = true;
// 清空占有线程
setExclusiveOwnerThread(null);
}
// 设置状态变量的值
setState(c);
return free;
}
private void unparkSuccessor(Node node) {
// 注意,这里的node是头节点
// 如果头节点的等待状态小于0,就把它设置为0
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
// 头节点的下一个节点
Node s = node.next;
// 如果下一个节点为空,或者其等待状态大于0(实际为已取消)
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);
}
总结
AQS是什么?
AQS是并发容器J.U.C(java.util.concurrent)下locks包内的一个类。它实现了一个FIFO的队列。底层实现的数据结构是一个双向链表。
AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。
为什么ReentrantLock默认采用的是非公平模式?
因为非公平模式效率比较高。
为什么非公平模式效率比较高?
答因为非公平模式会在一开始就尝试两次获取锁,如果当时正好state的值为0,它就会成功获取到锁,少了排队导致的阻塞/唤醒过程,并且减少了线程频繁的切换带来的性能损耗。
非公平模式有什么弊端?
非公平模式有可能会导致一开始排队的线程一直获取不到锁,导致线程饿死