这个先留着,这个内容有点多,待我这周结束之后来写。
面试官:AQS是什么?
我:AbstractQueuedSynchronizer抽象同步队列。说白了,就是个FIFO双向队列。其内部通过节点head和tail记录对首和队尾元素。
state:在AQS中维持了一个单一的状态信息state,可以通过getState、setState、compareAndSetState函数修改其值。
-
ReentrantLock:state可以用来表示当前线程获取锁的可重入次数。
-
ReentrantReadWriteLock:state的高16位表示读状态,也就是获取该读锁的次数,低16位表示获取到写锁的线程的可重入次数。
-
semaphore:state用来表示当前可用信号的个数。
-
CountDownlatch:state用来表示计数器当前的值。
对于AQS来讲,线程同步的关键是对状态值state进行操作。
在独占方式下获取和释放资源使用的方法:
void acquire(int arg);
void acquireInterruptibly(int arg);
boolean release(int arg);
在共享方式获取和释放资源使用的方法:
void acquireShared(int arg);
void acquireSharedInterruptibly(int arg);
boolean releaseShared(int arg);
使用独占方式获取的资源是与具体线程绑定的,就是说如果一个线程获取到了资源,就会标记是这个线程获取到了,其他线程再尝试操作state获取资源时会发现当前该资源不是自己持有的,就会在获取失败后被阻塞。(比如ReentrantLock)
对应的共享方式的资源与具体线程是不相关的,当多个线程去请求资源时通过CAS方式竞争获取资源,当一个线程获取到了资源后,另外一个线程再次去获取时如果当前资源还能满足它的资源,则当前线程只需要使用CAS方式进行获取即可。(比如semaphore)
看一下acquire方法:
// tryAcquire 具体的子类去实现,并维护state的状态
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 如果失败标记状态,入队
selfInterrupt();
}
看一下release方法:
// tryRelease 具体的子类是实现,并设置state的状态
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 调用unpark唤醒队列的线程,并调用tryAcquire尝试,看是否需要,如果不需要,继续挂起
return true;
}
return false;
}
acquireShared和releaseShared和上面的方法的思想差不多