Skip to content

Latest commit

 

History

History
69 lines (49 loc) · 2.63 KB

AQS.md

File metadata and controls

69 lines (49 loc) · 2.63 KB

AQS

这个先留着,这个内容有点多,待我这周结束之后来写。

面试官:AQS是什么?

原理

我:AbstractQueuedSynchronizer抽象同步队列。说白了,就是个FIFO双向队列。其内部通过节点head和tail记录对首和队尾元素。

state:在AQS中维持了一个单一的状态信息state,可以通过getStatesetStatecompareAndSetState函数修改其值。

  • 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和上面的方法的思想差不多