源码AbstractQueuedSynchronizer.java
同步队列 入口函数为 acquire 调用 独占锁:
如果tryAcquire失败.那么会一直等待直到成功.是一种阻塞锁.
因为acquire是一个阻塞锁.所以队列中有多个等待者,就有多少个线程.大体来说下过程
1.首先某个线程发起acquire获取锁的请求.如果成功很简单资源加锁就完了.主要是失败的处理.
2.如果请求锁失败,那么这时候会新生成一个Node对象包裹请求参数.加到队列尾部,值得注意的是这个队列是一个FIFO,有两个Node全局变量一个记录着head,一个记录着tail.
3.新加入的Node把prev指向tail,tail把next指向Node,最后把Node赋值给tail
4.每个等待的node对象.都在不停的判断它的前一个Node是不是head,如果是则它就不停的尝试获取锁(head拥有锁,只有等到它释放)
5.如图第二个节点,如果能成功的获取资源锁,则证明head已经释放了锁.那么这个时候第二个节点就把不客气的把自己弄成head.那么它的prev指向肯定就得为null.而之前的head这个时候已经没什么用了.把它的next指向质为null.这样互相引用就没了,应该更容易GC了.虽然GC能够处理这种相互引用,但是应该有代价吧.
6.重复上述过程. 下面对代码截图,对没说到的部分再说下
起点函数就是acquire,通过它来获取锁.
tryAcquire是个空函数.需要继承者来重载,这里来看下acquireQueued实现
第一个if基本就是上面说的那些.看第二个if高亮的函数定义
这里上面就没说到.waitStatus是一个等待状态,这里new Node对象里并没有赋值,那么默认值就这0.看它的几种状态的定义
看到基本都是小于0的,只有一个CANCEL = 1 .要注意的是,这个CANCEL是程序内部自己赋值的,也就是说当前线程被终止了.这个由cancelAcquire方法赋值.这个方法是个私有方法.而且从原理上不允许通过acquire后,再手动通过cancelAcquire取消调用.只能一直等待.否则的话这个node的for死循环线程一直存在,造成内存问题.
另外acquire在执行过程中是不能被中断的.有中断只会被置标记.执行完之后,会中断我们调用acquire那个线程.
所以一旦线程调用了acquire来请求锁,那么线程将一直阻塞直到获取锁.这期间没有任何办法来打断这个逻辑.所以如果我们想能够控制获取锁的时机,怎么办呢.那就是AQS的条件队列了.请看下篇的条件队列分析.
用acquireInterruptibly来中断,这个采用捕获InterruptedException这个异常,来达到中断的目地.
共享锁acquireShared .原理同上,只不过,独占锁,资源只有一个.而共享锁,有拥有多个资源.在资源没用完之前不会进行等待队列.资源用完时,一样处理.