涉及到锁,那么必定涉及到等待。AQS就是用来做锁和排队使用。这里有个小故事分享一下:
张三在某大型商场跟女朋友逛街,突然尿意来袭急奔厕所,发现坑位空位立马进入锁上大门。
三分钟后,李四走进厕所发现有一个坑位,过去拉门发现大门紧锁并且里面大喊有人,李四没办法只能旁边找了个凳子坐了下来,刚坐下来发现还有个人,李四心想完了还有人排在我前面,结果那人说自己是锁魂典狱长不是去上厕所的让他大可放心,李四才稍微放下了心继续看了下坑位大门紧闭,转头问典狱长:“里面真的有人吗?”,“有人!”,典狱长说道。听到这里李四还不放心,又问了一遍有人吗,典狱长不耐烦的说:“我说了有人,你问几遍了!”。这时候李四才真正的坐了下来。但是典狱长耐心消失殆尽 …
这时候又过了三分钟,只见进来一个叫王五的人,他跟李四做了一样的操作,然后坐到了李四旁边。这时候万众瞩目张三把门打开从坑位里出来了,典狱长一看两眼放光,耐心又回来了,心想我终于可以离开这破地方了。他拉着李四说:老哥,坑位有了!坑位有了!李四一听直接起身去占领坑位。完全不顾身后留下的魂魄,典狱长微微一笑把位置交给了李四的魂魄然后扬长而去,从此李四的魂魄变成了典狱长,王五上位了…
栗子
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,4,10,TimeUnit.SECONDS,
new LinkedBlockingQueue(500),new DefaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());
threadPoolExecutor.execute(()->{
lock.lock();
try {System.out.println(Thread.currentThread().getName() + " 张三占领了坑位");
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {lock.unlock();}
});
threadPoolExecutor.execute(()->{
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 李四占领了坑位");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {lock.unlock();}
});
threadPoolExecutor.execute(()->{
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 王五占领了坑位");
}finally {lock.unlock();}
});
}
李四抢坑位
java.util.concurrent.locks.ReentrantLock.class
//抢占坑位
public void lock() {
sync.lock();
}
//------------------------------
//抢占坑位
final void lock() {
//张三来的时候坑位门没有关,直接进坑位
if (compareAndSetState(0, 1))
//这里表示张三占住了坑位
setExclusiveOwnerThread(Thread.currentThread());
else
//李四来了坑位没了
acquire(1);
}
//------------------------------
//占坑位
public final void acquire(int arg) {
//李四想抢坑位
if (!tryAcquire(arg) &&
//李四被迫坐下
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//------------------------------
//尝试抢占坑位
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;
}
//------------------------------
//李四坐下
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(node);
return node;
}
//------------------------------
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;
}
}
}
}
//------------------------------
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);
}
}
//------------------------------
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
//李四第二遍询问典狱长,典狱长直接说有人,你小子给我坐下!
return true;
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.
*/
//李四一直问典狱长不高兴了。状态变成了-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
//老典狱长跑路了,李四的灵魂留下当了典狱长
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
张三释放坑位
//张三释放坑位
public void unlock() {
sync.release(1);
}
//------------------------------
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;
}
//------------------------------
//李四觉醒!
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
//典狱长两眼放光(因为典狱长可以跑路了)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
//s就是李四
Node s = node.next;
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);
}
总结
其中张三线程先来,直接就使用了资源,并且站住坑位 status + 1 , 坑位setExclusiveOwnerThread 设置成张三本人,之后李四来了,发现坑位计数器status有人,并且exclusiveOwnerThread 还不是自己,李四乖乖坐下,系统分配了一个傀儡Node 给李四当头结点head,让李四当尾节点tail,傀儡Node和李四相互有pre和next,等到张三走了status - 1,setExclusiveOwnerThread = null 。李四上位status + 1 ,setExclusiveOwnerThread = 李四,傀儡死掉,李四的Node变成了傀儡Node。
上面的代码是不公平的占坑位,因为每个人进来都是先抢坑位,不管有没有人在排队。
实现公平锁的方式就是占坑位的时候先看看有没有人在等坑位即可。其余都一样操作。
未完待续。。。