在Java中进行同步的方法包括使用同步关键字进行同步访问,以及在Java 5之后位于java.util.concurrent.locks包下的方法
实现同步访问的另一种方法是锁定。 今天,就Lock的实现类ReentrantLock在公平锁定模式下的实现方法进行说明。
java可重新锁定-重复锁定实现详细信息
ReentrantLock支持两种获取锁定的方法:公平模型和非公平模型。 在继续之前,让我们把故事元素转换成程序元素。
首先,让我们谈谈公平的摇滚模式:
初始化时,state=0表示没有人被剥夺打水权。 此时,村民A来打水(A线程要求钥匙),占打水权,state 1,如下:
线程a获得了锁定,获得了state原子性1。 此时,状态更改为1,a线程继续执行其他任务。 随后,村民b也试图打水(线程b请求锁定)。 线程b无法获取锁定,将生成节点并将其排队。 下图:
初始化时,将生成空的头节点,然后是b线程节点。 此时,如果线程a请求锁定,是否需要排队? 当然答案是否定的。 否则,就这样陷入死锁。 A再次要求钥匙,意味着在打水的过程中,同一家人来打水了。 我有特权。 此时的状态如下图所示。
到此为止,我想您已经知道了什么是锁住了。 一个线程获得锁定后,只需通过再次去获取同一锁定来累计状态值。 线程a释放一次锁定,情况就是这样。
只要减少状态值,线程a就会释放所有此锁定,并将状态值减少到0,其他线程就可以获取锁定。 当a完全解除锁定时,state将返回0,通知队列唤醒b线程节点,以便b再次冲突锁定。 当然,如果b线程后面有c线程,c线程将继续休眠并通知c线程,除非b执行完毕。 请注意,当线程节点被唤醒并锁定时,相应的节点将从队列中删除。
非公平锁定模型
如果理解前面提到的公平锁定模型,非公平锁定模型也将非常容易理解。 在线程a执行后,唤醒线程b需要时间,并且线程b唤醒后再次竞争锁定,因此如果线程c在切换过程中到来,则线程c可能获得锁定,而如果c获得锁定,则b乖乖地继续休眠这里不要画画了。
可重入锁
如果锁有重新输入的可能性,则称为可重新输入锁。 这是一种可重读的锁,如同步和延迟锁定,可重读性实际上表明了锁的分配机制。 不是基于方法调用的分配,而是基于线程的分配。 举个简单的例子,如果一个线程运行到某个synchronized方法(例如method1),并且在method1中调用另一个synchronized方法method2,则线程不会重新申请锁定,而是
看看下面的代码就知道了。
class MyClass {
公共同步语音方法1 (
method2(;
}
公共同步语音方法2 (
}
}
上述两个方法method1和method2都是同步限定的,但是如果线程a在某个时间点运行到method1,则线程a获得了该对象的锁定,而method2也是同步的如果同步不可重新输入,则线程a会继续等待永远无法获取的锁定,因为线程a已经具有该对象的锁定,并且正在申请获取该对象的锁定
另一方面,由于同步和锁定都具备重新输入的可能性,因此不会发生上述现象。
接下来,开始真正的源代码分析。
公共语言(布尔故障) {
sync=fair? newfairsync (: newnonfairsync );
}
ReentrantLock的构造方法现在只看公平锁。 实现FairSync
staticfinalclassfairsyncextendssync {
privatestaticfinallongserialversionuid=-3000897897090466540 l;
final void锁定
acquire(1;
}
//*
* fairversionoftryacquire.don ' tgrantaccessunless
* recursivecallornowaitersorisfirst。
*/
保护性金融
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;
}
}
当调用lock方法时,会调用acquire(1)方法,获取state,state等于0表示没有线程获取到锁,接下来会判断是否有等待的线程,如果没有等待线程和进行cas操作state成功,则设置当前独占线程为当前线程,返回true,获取锁成功。后面有个else条件就是可重入锁的实现,如果获取锁失败,则判断当前独占锁线程是否为当前线程
,如果是当前线程,则将state进行加一操作,并返回true,获取锁成功。否则获取锁失败。
注意事项:state 使用了volatile关键字,使state被线程修改时,及时的被其他线程读取到最新的值。
AbstractQueuedSynchronizer
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
尝试获取锁,当获取锁失败后,将当前线程放入等待队列中,然后开始循环去获取锁操作。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
如果已经没有线程获取锁的时候,则返回true,当前锁线程进行interrupt操作,