今日学习(自律天数:4):AQS源码+Semaphore源码+手写小demo

112 阅读3分钟

今日学习

AQS源码全路线+Semaphore路线+基于AQS写自己的Semaphore。深入理解了AQS队列操作和对资源量的把控。昨天电脑没电了。

1. AQS

以下是我自定义一个自己的锁,能实现简单锁逻辑,什么重入中断就没有了啊。

static class MoXiuLock extends AbstractQueuedSynchronizer {
    @Override
    protected boolean tryAcquire(int arg) {
        return compareAndSetState(0, 1);
    }

    @Override
    protected boolean tryRelease(int arg) {
        return compareAndSetState(1, 0);
    }
    
    public void lock() {
        acquire(1);
    }
    
    public void unLock() {
        release(1);
    }
}

如果我们需要实现一个独占锁,只要我们重写tryAcquire和tryRelease就可以了,为什么呢? 那我们只需要去看acquire和release做了什么就可以了

public final void acquire(int arg) {
    //尝试获取锁如果获取了就直接返回
    if (!tryAcquire(arg) &&
        //没有获取需要入队
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        //响应中断逻辑
        selfInterrupt();
}

深入addWaiter

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;
}

加入队列后acquireQueued逻辑!

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;//返回是否有中断
            }
            //修改waitState  
            if (shouldParkAfterFailedAcquire(p, node) &&
                //阻塞不会让他一直去for消耗cpu资源
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

深入shouldParkAfterFailedAcquire

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.
         */
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //他必须把前一个结点的waitStatus弄为SIGNAL才能去阻塞
    }
    return false;
}

AQS其实就是帮我们完成了入队出队的操作和阻塞线程的操作,这些都是一个并发安全框架必要且类同的东西,所以就有了AQS这个抽象队列同步器帮我们干这些雷同的操作。

public final boolean release(int arg) {
    if (tryRelease(arg)) {//这里掉我们自己那个方法
        Node h = head;
        if (h != null && h.waitStatus != 0) //之前我们不是有结点把前一个结点的waitStatus弄为了SIGNAL这里就派上用了
            unparkSuccessor(h);//唤醒下一个线程
        return true;
    }
    return false;
}

AQS呢还是比较简单的,大家也看到了他只有头结点才能获取到锁,这就是他的公平策略了,但如果你是非公平锁的话呢他就只是差了一次队而已。

1. Semaphore 信号量

看以下代码实现了一个简单的自定义信号量

static class MoXiuSemaphore extends AbstractQueuedSynchronizer {

    public MoXiuSemaphore(int resource) {
        setState(resource);
    }

    @Override
    protected int tryAcquireShared(int arg) {
        int state = getState();//拿到当前资源量
        if(state == 0) {
            return -1;
        }
        //当前资源数需要减
        int nextState = state - arg;
        //cas操作实现抢锁
        if (compareAndSetState(state, nextState)) {
            return nextState;
        }
        //抢不到的就返回-1了
        return -1;
    }

    @Override
    protected boolean tryReleaseShared(int arg) {
        for(;;) {
            int state = getState();
            if (compareAndSetState(state, state+arg)) {
                break;
            }
        }
        return true;
    }
}

和之前的独占锁不同 这里使用的是都是加了结尾Shared的方法具体的源码读者自己去看吧,其实看懂了AQS的代码的话,这个是非常之简单的。