学习自
https://blog.csdn.net/yanyan19880509/article/details/52345422
一家人可以不用排队——重入
非公平实现:如果井没人用,正在交接,可以插队一波;如果井有人用,只能乖乖排队
对应一下
默认的非公平锁——lock与unlock
lock
final void lock() {
if (compareAndSetState(0, 1))//如果没人用,我用用试试看,非公平!
setExclusiveOwnerThread(Thread.currentThread());
else//结果不行
acquire(1);
}
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;
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//队尾添加节点,然后阻塞线程,但是在阻塞前还会争取一下
selfInterrupt();
}
unlock
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
公平就不看了, 大同小异
condition
await
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();//新建一个节点并加到等待队列中去
int savedState = fullyRelease(node);//释放当前锁
int interruptMode = 0;
while (!isOnSyncQueue(node)) {//如果当前节点已经时候Node.CONDITION了
LockSupport.park(this);//阻塞当前线程
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
signal
public final void signal() {
if (!isHeldExclusively())//如果不是当前线程独占中
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);//唤醒第一个
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)//null统一
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&//如果唤醒失败,且成员变量firstWaiter不为空,那就再次进行循环
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))//设置状态
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);//唤醒
return true;
}
写到一半,发现这样不行,还是讲模型吧!
lock unlock
AQS的state,0代表无人使用,1代表1个人在使用,1以上代表单个线程重入了。
在lock的时候,因为默认是非公平的,所以会尝试性地cas 0-1,也就是如果没人用,会尝试性地插队。
插队失败,就会生成一个CONDITION的Node,添加到队列中,并休眠自己。
unlock的时候,state减去1,如果发现到0了,那么就会唤醒链表头部的Node。
(如果是公平的话,那么lock的时候,就不会插队了)
await signal
在await的时候,会把所有线程都加入等待队列中去,并且休眠这些线程。同时state-1。在signal的时候,会把这些节点,1个或者所有都加入到释放队列中,这个时候还不会唤醒,因为唤醒线程还在使用资源,state>=1。知道唤醒线程unlock或者await的时候,才会开始唤醒这个释放队列,他们又开始重新竞争。(此外,只有当前获取资源的线程才能await与signal)
condition更可以是多条件
new Thread(new Runnable() { @Override public void run() { //加锁 reentrantLock.lock(); try { Log.i(TAG, "我睡着了"); //沉睡 condition1.await(); condition2.await(); Log.i(TAG, "我睡醒了"); } catch (InterruptedException e) { e.printStackTrace(); } finally { //解锁 reentrantLock.unlock(); } } }).start();
其实和单条件是差不多的。只要你当前在使用资源,而且调用了condition.await,那你就得睡。对你生效的只有一个锁,因为你已经睡着了,执行不到另一个condition.await,所以一次性最多对一个condition睡觉。当你被唤醒了,再执行下面的await,然后又睡了。
此外,这些不同的condition,俨然是不同的队列。
lock interruptibly
为何他能响应中断呢?假如该线程被interrupt了,那么中断标志位也响应改变了。在lock interruptibly的时候,会第一时间检查中断标记位,如果有,直接抛出中断异常。当然也有可能在开始排队后,被中断。这个时候回阻塞在park方法上,一旦被唤醒,会再次检测中断,如果已被标记位中断,也是直接抛出异常。
read write lock
这个我了解的不多,只知道分为读锁和写锁,其中读是可以存在并发的。