1.可重入锁
实现机制
final void lock() { //CAS的方式获取锁,设置status = 1
if (compareAndSetState(0, 1)) //设置为当前线程占有
setExclusiveOwnerThread(Thread.currentThread());
else //判断是否需要阻塞
acquire(1); //如果当前线程占有status + 1, 否则尝试占有锁
}
2.多condition锁
condition队列,转移到线程阻塞队列
构建多个等待队列
3.可以设置是否为公/非公平锁
非公平锁,无需判断自己是否进入队列
公平锁会判断自己是否是头节点来保证fifo,也就是必须入队列且自己为头节点
/**
在所有线程被LockSupport挂起之后,那么不论是公平锁,还是非公平锁,都是默认唤醒队列中当前线程的下一个线程
然而,非公平锁的非公平体现在,tryAcquire()方法上!!!
我们再回头看一下fairSync和unfairSync的tryAcquire方法
核心点来了:
·······························································
公平锁,若队列不为空,没入队的线程不得抢锁,
非公平锁,若队列不为空,没入队的线程却可以抢锁,
这时后到的线程可能先抢到锁,即不公平。
·······························································
读者可能还会有点迷惑,那是没入队的不可以抢锁,没入队的可以抢锁
我们再仔细体会这两个方法。
·················
如果是非公平锁,记还没进aqs队列的线程为A,在运行的线程为B,队列中B的next记为B.next。
那么这个时候A和B.next是可以同时竞争这个锁的。
如果是非公平锁,记还没进aqs队列的线程为A,在运行的线程为B,队列中的next记为B.next。
那么这个时候tryAcquire的时候,因为有队列中还有节点,那么这个tryAcquire方法一定是false。
因此这个A线程一定会进入aqs队列然后不断的for循环tryAcquire,如果队列中就剩它这个A节点了,那么在tryAcuqire中
才会激活。或者在几次for循环后被LockSupport给挂起,等待他的前驱线程来唤醒它。
·················
*/
//这是公平锁
protected final boolean tryAcquire(int acquires) {
if (getState() == 0 && !hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//这是非公平锁
protected final boolean tryAcquire(int acquires) {
if (getState() == 0 && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
因此ReentrantLock 只有头节点线程A和新加入的线程B会产生竞争,也就是非公平。
4.lock的等待机制使用
加锁时循环四次尝试拿取锁,否则直接
LockSupport.park(); 精准到指定线程等待。