ReadWriteLock之公平锁解析(三)

2021-11-23  本文已影响0人  436048bfc6a1

接下来跟着上一篇, 解析情景五和情景六

情景五

写读读

public static void main(String[] args){
    final Printer printer = new Printer();

    Thread thread1 = new Thread(){
        @Override
        public void run() {
            try {
                printer.write("test1");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };
    Thread thread2 = new Thread(){
        @Override
        public void run() {
            try {
                printer.read("test2");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };
    Thread thread3 = new Thread(){
        @Override
        public void run() {
            try {
                printer.read("test3");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };
    thread1.start();
    thread2.start();
    thread3.start();
}
public void lock() {
    sync.acquire(1);
}
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
    // thread-0
    Thread current = Thread.currentThread();
    // c = 0
    int c = getState();
    // w = 0
    int w = exclusiveCount(c);
    // 不走此分支
    if (c != 0) {
        ......
    }
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}
static final class FairSync extends Sync {
    final boolean writerShouldBlock() {
        return hasQueuedPredecessors();
    }
}
public final boolean hasQueuedPredecessors() {
    Node t = tail; 
    Node h = head;
    Node s;
    // h和t都为null, 返回false
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

回到tryAcquire方法

protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    int c = getState();
    int w = exclusiveCount(c);
    if (c != 0) {
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        setState(c + acquires);
        return true;
    }
    // writerShouldBlock方法返回false
    if (writerShouldBlock() ||
        // 在此CAS假设是成功的
        // 所以不会走此分支
        !compareAndSetState(c, c + acquires))
        return false;
    // 设置获得写锁的线程为thread-0
    setExclusiveOwnerThread(current);
    // 返回true
    return true;
}

此时线程1获得写锁, 线程2开始执行

public void lock() {
    sync.acquireShared(1);
}
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
protected final int tryAcquireShared(int unused) {
    // thread-1
    Thread current = Thread.currentThread();
    // c = 1
    int c = getState();
    // 此时线程1获得写锁
    // 并且当前线程是线程2
    // 所以会进入此分支
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    ......
}

接下来回到acquireShared方法

public final void acquireShared(int arg) {
    // tryAcquireShared返回-1, 进入此分支
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
private void doAcquireShared(int arg) {
    // 向队列中加入代表当前线程的节点
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            // 线程2是第一个被阻塞的
            // 所以其前序节点就是head
            // p此时就是head
            final Node p = node.predecessor();
            if (p == head) {
                // 在进行阻塞或其他操作前再确认下是否可以获得锁
                // 此时r = -1, 也就是确实无法获取到锁
                int r = tryAcquireShared(arg);
                // 不走此分支
                if (r >= 0) {
                    ......
                }
            }
            // 第一次循环不会阻塞, 第二次循环会被阻塞
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

此时线程2就被阻塞

此时, 线程3开始执行

// ReadLock
public void lock() {
    sync.acquireShared(1);
}
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
protected final int tryAcquireShared(int unused) {
    // thread-2
    Thread current = Thread.currentThread();
    // c = 1
    int c = getState();
    // 获得锁线程为thread-0, 进入此分支
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    ......
}

回到acquireShared方法

public final void acquireShared(int arg) {
    // tryAcquireShared返回-1, 进入此分支
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
private void doAcquireShared(int arg) {
    // 此时将thread-2加入队列尾部
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            // p代表被阻塞的线程thread-1, 不是head
            // 不走此分支
            if (p == head) {
                ......
            }
            // 在第二次循环的时候会阻塞自身
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

情景六

写读写

线程1和线程2和情景五一样, 主要解析线程3

public void lock() {
    sync.acquire(1);
}
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
    // thread-2
    Thread current = Thread.currentThread();
    // c = 1
    int c = getState();
    // w = 1
    int w = exclusiveCount(c);
    if (c != 0) {
        // 虽然w = 1
        // 但是加锁线程为thread-0, 可以走此分支
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        ......
    }
    ... 
}

接下来返回acquire方法

public final void acquire(int arg) {
    // tryAcquire返回false
    if (!tryAcquire(arg) &&
       // 将对应当前线程的节点加入缓存队列中
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

此时线程3被阻塞

上一篇下一篇

猜你喜欢

热点阅读