目录
非公平锁的实现:
在看公平锁的实现之前,我们先来看ReentrantLock中一个静态内部类Sync,Sync是一个抽象的静态内部类,集成了AbstractQueuedSynchronizer抽象类.源码如下:
Sync是ReentrantLock实现同步控制的基础,非公平锁和公平锁都是其子类,使用AQS的state变量来代表锁被持有的次数
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
//获取锁的方法,在子类中实现的主要原因就是对于公平锁而言,允许
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is
* implemented in subclasses, but both need nonfair
* try for trylock method.
*/
//非公平锁的tryLock,tryAcquire 方法被子类实现,但是都需要非公平锁尝试tryLock方法
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取state变量值,也就是获取是否锁已经备被线程所持有
int c = getState();
//c==0代表锁未被任何线程所占有,CAS获取锁,获取成功后,将exclusiveOwnerThread属性设置为当前线程并返回
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果当先线程已经持有锁,那么将state加1并设置state返回
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;
}
//释放锁操作
protected final boolean tryRelease(int releases) {
//先将state减去releases
int c = getState() - releases;
//如果当前线程不是正在持有锁的线程,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//free局部变量标识是否已经释放锁
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
//
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes this lock instance from a stream.
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
非公平锁获取锁流程
1 调用lock方法,lock方法首先会进行CAS操作,将state属性尝试设置为1,如果成功,则代表获取到锁,将exclusiveOwnerThread属性设置为当前获取锁的线程。
2 如果线程CAS失败,则调用AQS的acquire方法,去获取锁,tryAcquire(1)是子类自己的实现,从下面可以知道,调用了nonfairTryAcquire方法,
nonfairTryAcquire方法中也是首先去判断state属性,如果等于0,则CAS获取锁,获取失败的话返回false,如果锁已经被占有且是当前线程,那么将state属性+1,否则返回false。如果返回了false,也就是获取锁失败,调用addWaiter方法,将当前线程都造成节点加入同步队列,
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
/**
* Performs non-fair tryLock. tryAcquire is
* implemented in subclasses, but both need nonfair
* try for trylock method.
*/
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;
}
3 调用addWaiter方法将当前线程及其信息构造成节点入队。在这里,如果此时同步队列为空,则先进行初始化操作,先设置一个伪节点作为头结点
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
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;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//如果同步队列为空,enq方法不停的循环初始化同步队列,直至将节点入队返回
enq(node);
return node;
}
4 线程节点入队以后,会调用acquireQueued方法,该方法中,线程会不停的循环,首先判断其前驱节点是否是头结点,如果是头结点,则尝试获取锁,如果获取锁成功,当前线程所在节点被设为头结点。这里可以看出,线程获取锁的条件是:
线程所在的节点的前驱节点是头结点才有资格去获取锁。
对于前驱节点不是头结点的线程而言,调用shouldParkAfterFailedAcquire方法和parkAndCheckInterrupt方法,下面是其实现。该方法回去检查并且更新获取锁失败的线程等待状态,如果线程阻塞,则返回true,从这个方法名称就可以看出,这个方法的作用就是在线程获取锁失败后,阻塞线程。
/**
* Checks and updates status for a node that failed to acquire.
* Returns true if thread should block. This is the main signal
* control in all acquire loops. Requires that pred == node.prev
*
* @param pred node's predecessor holding status
* @param node the node
* @return {@code true} if thread should block
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//获取当前线程的前驱节点的等待状态
int ws = pred.waitStatus;
//如果前驱节点的等待状态是signal,也就是唤醒后继线程.这个时候线程就会放心,当前驱节点释放锁后,自然会唤醒它,直接返回true
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
//如果等待状态大于0,说明其前驱节点已被取消,通过do while循环将其前驱节点设置为等待状态不为0的节点作为前驱节点。
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
/**
* Convenience method to park and then check if interrupted
*
* @return {@code true} if interrupted
*/
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
/**
* Sync object for non-fair locks
* 非公平锁的Sync实现
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
//非公平的获取锁的方法,
final void lock() {
//CAS获取锁,将state属性设置为1,若CAS成功,则将exclusiveOwnerThread属性设置为当前线程.
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else//CAS失败的情况下,调用AQS的acquire方法,
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
以上就是非公平锁的获取流程.
公平锁的实现:
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
//tryAcquire方法的公平锁版本,虽然是公平的,但是不保证一定会获取锁,除非是递归调用或者是第一个节点或者是前面无等待线程
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//这里是实现公平的关键,hasQueuedPredecessors方法判断当先线程前面是否还有等待线程,如果有等待线程,则不去竞争获取锁
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;
}
}
流程:
1 调用lock方法,间接调用AQS的acquire方法,在acquire方法中,首先调用公平锁自己实现的tryAcquire方法,
其他和非公平锁无太大区别
非公平锁和公平锁的区别:
非公平锁的公平锁的区别
区别就在于tryAcquire方法
1 当锁未被任何线程持有的时候,对于公平锁而言,会检查是否有等待队列,
这里有两种情况:
a 新晋线程,对于新晋线程而言,要先去判断下等待队列是否有其他线程已经在等待,如果有其他线程在等待,则不去竞争锁
b 对于已经处于等待队列中线程而言,也要其前面是否有等待线程,
在判断等待队列中是否已经有线程在当前线程前的时候,有这样几种情况:会促使线程去获取锁:
a 等待队列为空,直接竞争获取锁
2 当锁已经被其他线程锁持有,非公平锁中,线程会加入等待队列,公平锁中,线程也会加入等待队列.这一点,公平锁和非公平锁是一样的
对于加入到等待队列中的线程而言,公平锁和非公平锁
并无任何区别
主要区别就是对于新加进来的线程,非公平锁中,新加进来的线程会和等待队列中的线程一同竞争锁,可能会导致等待队列中线程获取不到锁,
但是在公平锁中,对于新加进来的线程,会先去检查等待队列中是否已经有线程在等待,若有,则不去竞争锁,加入等待队列
2
常用方法:
tryLock() | |
| 释放锁的操作, |
| 返回Lock实例对应的Condition实例,返回的Condition实例和在使用对象监视器锁的时候,Object对象的wait()、notify()、notifyAll()方法一样的功能。 如果锁实例未被任何线程持有,则Contidition对象的await和signal方法会抛出异常 如果线程在等待过程中被中断,然后等待状态也会被终止,然后抛出中断异常,中断状态被清空 等待线程会以FIFO的顺序被唤醒 线程从等待状态返回再次获取锁的顺序和线程初始获取锁的顺序是一样的,默认情况下也不会指定任何线程,但是对于公平锁而言,线程要等待更长时间
|
| 查询当前线程获取锁的次数,也就是所谓的重入了几次 |
| 如果锁被当前写线程所持有,则返回true |
|