concurrent-锁

AQS
ReentrantLock
ReentrantReadWriteLock

AQS

全名叫AbstractQueuedSynchronizer。
image

public abstract class AbstractQueuedSynchronizer
        extends AbstractOwnableSynchronizer
        implements java.io.Serializable {
    //链表队列头和尾
    private transient volatile java.util.concurrent.locks.AbstractQueuedSynchronizer.Node head;
    private transient volatile java.util.concurrent.locks.AbstractQueuedSynchronizer.Node tail;
    
    //核心属性,竞争的资源指的就是抢占这个属性
    private volatile int state;

   //当前持有state的线程
    private transient Thread exclusiveOwnerThread;

    static final class Node {//每个node代表一个线程
        //标记线程是因为获取共享资源失败而进入队列的
        static final java.util.concurrent.locks.AbstractQueuedSynchronizer.Node SHARED = new java.util.concurrent.locks.AbstractQueuedSynchronizer.Node();
        //标记线程是因为获取独占资源失败而进入队列的
        static final java.util.concurrent.locks.AbstractQueuedSynchronizer.Node EXCLUSIVE = null;

        //表示线程因为中断或者等待超时,需要从等待队列中取消等待
        //该状态node会自动被jvm回收
        static final int CANCELLED = 1;
        //已占有锁的线程释放时会通知该状态节点去抢占资源
        static final int SIGNAL = -1;
        //标识该节点在condition队列中。
        // 备注:lock的每个condition都有一个阻塞队列,当调用了signal方法后,队列中的节点会移动到clh队列中
        //类似sychronize的waitSet
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;

        volatile int waitStatus;

        //队列是双向链表
        volatile java.util.concurrent.locks.AbstractQueuedSynchronizer.Node prev;
        volatile java.util.concurrent.locks.AbstractQueuedSynchronizer.Node next;

        volatile Thread thread;

        java.util.concurrent.locks.AbstractQueuedSynchronizer.Node nextWaiter;

    }

    //用来存放condition阻塞队列
    public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        /**
         * First node of condition queue.
         */
        private transient AbstractQueuedSynchronizer.Node firstWaiter;
        /**
         * Last node of condition queue.
         */
        private transient AbstractQueuedSynchronizer.Node lastWaiter;
    
    }

}

返回顶部

ReentrantLock

 public static void main(String[] args) {

        ReentrantLock lock = new ReentrantLock();//构造器

        lock.lock();//加锁

        lock.unlock();//解锁

    }

构造器:

public class ReentrantLock implements Lock, java.io.Serializable {
    //两个实现类:NonfairSync-非公平锁,FairSync-公平锁
  //NonfairSync和FairSync都是ReentrantLock内部类
    private final java.util.concurrent.locks.ReentrantLock.Sync sync;
    public ReentrantLock(boolean fair) {
        sync = fair ? new ReentrantLock.FairSync() : new ReentrantLock.NonfairSync();
    }
    public ReentrantLock() {//默认是非公平锁
        sync = new ReentrantLock.NonfairSync();
    }
}

我们先分析非公平锁。

这里先总结一下lock方法的全部流程:
1、首先通过cas尝试获取state,如果成功,直接更新state持有线程为当前线程
2、如果失败,先判断一下state是否为0,如果为0,再次尝试通过cas获取state,成功更新state持有线程
3、如果失败,则判断一下state当前持有线程是否为当前线程,如果是,则可以重入,直接更新重入次数+1,结束流程
4、如果失败,也不可以重入,那么将当前线程封装到node中,然后通过尾插法插入到队列
插入队列逻辑:
1、首先判断当前队列尾节点是否为null,如果不为null,则通过cas插入尾部
2、如果当前队列尾节点为null,则初始化一个队列,然后通过cas插入尾部。
如果当前队列尾节点不为null,但是在第一步cas插入失败,则通过cas方式循环不停向尾部插入。
总之这一步完成后节点一定是插入到队列尾部了。
接下来的逻辑是在一个无限循环内部:
1、首先判断一下当前节点的前驱节点是否为队列头部,如果是头部,则该节点再次尝试获取state,如果成功,则将当前节点更新为队列头部。
2、如果不是头部,或者获取state失败,则当前节点通过LockSupport.park(this);方法阻塞自己。
注意:这是在一个循环里阻塞的,当被唤醒时,循环会继续。

加锁:
ReentrantLock.NonfairSync.lock():首先cas方式尝试更新state,成功更新state持有者为当前线程,失败进入acquire方法。

//    ReentrantLock.NonfairSync
    final void lock() {
        if (compareAndSetState(0, 1))//cas方式更新state值为1
            setExclusiveOwnerThread(Thread.currentThread());//入口
        else
            acquire(1);//入口
    }

//    AbstractOwnableSynchronizer
    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

AbstractOwnableSynchronizer.acquire:

     //    AbstractOwnableSynchronizer
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

接下来分别看上面的四个方法:tryAcquire、acquireQueued、addWaiter、selfInterrupt。

tryAcquire:再次尝试直接获取state,如果失败在判断是否是锁重入

//    ReentrantLock.NonfairSync
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
    //    ReentrantLock.NonfairSync
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {//如果state没有线程占用
            if (compareAndSetState(0, acquires)) {//尝试获state
                setExclusiveOwnerThread(current);//获取state成功,更新锁持有线程为当前线程
                return true;
            }
        }
        //如果state已经有线程占用了,但是占用的线程为当前线程,就是锁重入
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;//更新锁重入次数 +1
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;//其他情况返回false
    }

addWaiter:创建一个node,添加到aqs队列尾部,如果失败(多线程竞争)通过循环cas方式知道成功为止。

 //    AbstractOwnableSynchronizer
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);

        Node pred = tail;//尾插法
        if (pred != null) {//如果队列原尾节点不为null,将当前节点插入到队列尾部
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //代码如果走到这里有两种情况,1:队列还未初始化即队列原尾节点为null。
        // 2:刚才向队列尾部插入节点失败,有其他线程竞争插入。即compareAndSetTail(pred, node)失败
        enq(node);//入口
        return node;
    }
    private AbstractQueuedSynchronizer.Node enq(final AbstractQueuedSynchronizer.Node node) {
        //该方法是往队列尾部插入节点
        // 当队列还未初始化时,会初始化一个队列,然后插入到尾部
        //当队列已经初始化,则通过cas方式,失败不停重试,向尾部插入节点
        for (;;) {//cas方式  失败不停循环
            AbstractQueuedSynchronizer.Node t = tail;
            if (t == null) { // 如果队列原尾节点为null,
                if (compareAndSetHead(new AbstractQueuedSynchronizer.Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

AbstractOwnableSynchronizer.acquireQueued:参数是刚才添加到队列中的node,该方法判断node的前驱节点是否是链表head,如果是,尝试获取state,如果成功,则更新node为链表head。
如果失败,则阻塞住自己,这是在一个循环中阻塞。等到state持有线程释放锁会唤醒阻塞线程,循环继续。

 //    AbstractOwnableSynchronizer
    final boolean acquireQueued(final AbstractQueuedSynchronizer.Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final AbstractQueuedSynchronizer.Node p = node.predecessor();//获取前驱节点
                if (p == head && tryAcquire(arg)) {//如果前驱节点是头节点,则尝试获取state
                    //如果获取state成功了,则将当前node(state持有线程的node)设置为链表的head
                    setHead(node);
                    p.next = null;
                    failed = false;
                    return interrupted;
                }
                //如果不是头节点或者获取state失败,则阻塞自己。
                //注意:这是在一个循环里阻塞的,当被唤醒时,循环会继续
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

锁释放

ReentrantLock.unlock:
1、首先将state值减去1,如果state大于0,证明锁重入,直接结束。如果state等于0,还要更新state当前持有线程为null
2、从头节点开始往下找,如果节点状态不为-1,则更新其状态为作废(jvm自动回收),直到找到第一个状态为-1的节点,然后调用唤醒。
这样就和上面在循环中阻塞连接起来了。

  //    ReentrantLock
public void unlock() {
    sync.release(1);
}
//AbstractOwnableSynchronizer
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            AbstractQueuedSynchronizer.Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;//考虑锁重入
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {//锁的进入次数为0
            free = true;
            setExclusiveOwnerThread(null);//更新state持有线程未null
        }
        setState(c);//如果锁的重入次数未降到0,则直接更新state次数-1就结束
        return free;
    }

 private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)//如果节点的状态小于0,则通过cas更新为0
            compareAndSetWaitStatus(node, ws, 0);

        Node s = node.next;//获取节点的next节点
        if (s == null || s.waitStatus > 0) {//如果next节点为null或者状态不为-1,则将next节点更新为null
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)//将最近的状态为-1的节点设置给s
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);//唤醒
    }

上面是非公平锁的分析,公平锁又是什么样的?

非公平锁尝试获取锁有以下几个地方:
请求线程来了马上尝试获取state -- 判断state=0,再次尝试获取state --尝试获取重入锁 -- 创建node添加到队列,如果node为第2个节点尝试获取state
非公平锁尝试获取锁有以下几个地方:
请求线程来了,先判断state=0,并且请求线程和第2个节点是同一个线程,尝试获取state -- 后面都一样

返回顶部

ReentrantReadWriteLock

  public static void main(String[] args) {
        ReadWriteLock lock = new ReentrantReadWriteLock();
        Lock readLock = lock.readLock();
        Lock writeLock = lock.writeLock();

        //读和读不互斥
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    readLock.lock();
                    Thread.sleep(3000);
                    readLock.unlock();
                    System.out.println("读锁释放");
                }catch (Exception e){
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    Thread.sleep(1000);
                    readLock.lock();
                    System.out.println("获取到读锁");
                    readLock.unlock();
                }catch (Exception e){
                }
            }
        }).start();
    }
public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
    private final java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock readerLock;
    private final java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock writerLock;
    final java.util.concurrent.locks.ReentrantReadWriteLock.Sync sync;
}
  //重入计数器,读锁 -- 可重入锁  ,  该属性用来记录每个线程重入读锁次数
    static final class HoldCounter {
        int count = 0;
        final long tid = getThreadId(Thread.currentThread());
    }

    //HoldCounter是用来记录一个线程的次数    readHolds存储所有线程的HoldCounter
    private transient ThreadLocalHoldCounter readHolds;
    static final class ThreadLocalHoldCounter//一个内部类的定义
            extends ThreadLocal<HoldCounter> {
        public HoldCounter initialValue() {
            return new HoldCounter();
        }
    }
    /** 最近一个成功获取读锁的线程的计数。这省却了ThreadLocal查找 缓存*/
    private transient HoldCounter cachedHoldCounter;

state是32位int类型的,ReentrantReadWriteLock将其拆分成两段,高16位用来表示读锁数量,低16位用来表示写锁数量。

分别分析四个方法:readLock.lock readLock.unlock writeLock.lock writeLock.unlock

readLock.lock

 //    ReentrantReadWriteLock
    public void lock() {
        sync.acquireShared(1);
    }
//AbstractQueuedSynchronizer
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

分别调用了tryAcquireShared和doAcquireShared。

ReentrantReadWriteLock.tryAcquireShared:该方法先判断state是否有写锁持有,如果有直接返回失败。如果没有,则通过循环cas方式更新读锁直到成功。
有几点需要注意:
1、第一个读锁单独缓存到firstReader,性能提升
2、通过ThreadLocal和HoldCounter类来存储每个线程锁state的值

//    ReentrantReadWriteLock
protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    int c = getState();
    //state写锁位段大于0,并且当前线程不是state持有线程,直接返回-1
    if (exclusiveCount(c) != 0 &&
            getExclusiveOwnerThread() != current)
        return -1;
    int r = sharedCount(c);//获取state写锁位段
    if (!readerShouldBlock() &&
            r < MAX_COUNT &&//
            compareAndSetState(c, c + SHARED_UNIT)) {
        //如果读锁数量未达到上线,并且cas获取state读锁成功
        if (r == 0) {
            //state的第一个读锁单独用firstReader来存储,增加性能
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            //当前线程就是firstReader
            firstReaderHoldCount++;
        } else {//通过ThreadLocal类型的HoldCounter来存储锁的次数
            HoldCounter rh = cachedHoldCounter;
            if (rh == null || rh.tid != getThreadId(current))
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
            rh.count++;
        }
        return 1;
    }//当上面代码获取锁失败后,这个方法会通过cas循环获取锁,直到成功。  逻辑同上
    return fullTryAcquireShared(current);
}
    //    ReentrantReadWriteLock
    final int fullTryAcquireShared(Thread current) {
        ReentrantReadWriteLock.Sync.HoldCounter rh = null;
        for (;;) {
            int c = getState();
            if (exclusiveCount(c) != 0) {
                if (getExclusiveOwnerThread() != current)
                    return -1;
                // else we hold the exclusive lock; blocking here
                // would cause deadlock.
            } else if (readerShouldBlock()) {
                // Make sure we're not acquiring read lock reentrantly
                if (firstReader == current) {
                    // assert firstReaderHoldCount > 0;
                } else {
                    if (rh == null) {
                        rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current)) {
                            rh = readHolds.get();
                            if (rh.count == 0)
                                readHolds.remove();
                        }
                    }
                    if (rh.count == 0)
                        return -1;
                }
            }
            if (sharedCount(c) == MAX_COUNT)
                throw new Error("Maximum lock count exceeded");
            if (compareAndSetState(c, c + SHARED_UNIT)) {
                if (sharedCount(c) == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {
                    if (rh == null)
                        rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        rh = readHolds.get();
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;
                    cachedHoldCounter = rh; // cache for release
                }
                return 1;
            }
        }
    }

AbstractQueuedSynchronizer.doAcquireShared:

//AbstractQueuedSynchronizer
private void doAcquireShared(int arg) {
    //首先添加节点到队列尾部,逻辑同ReentrantLock
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {//如果前驱节点是头节点,则尝试获取state
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            //如果不是头节点或者获取state失败,则阻塞自己。
            //注意:这是在一个循环里阻塞的,当被唤醒时,循环会继续
            if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

释放读锁

//ReentrantReadWriteLock
    public void unlock() {
        sync.releaseShared(1);
    }
//    AbstractQueuedSynchronizer
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

分别调用了两个方法:tryReleaseShared和doReleaseShared

ReentrantReadWriteLock.tryReleaseShared:更新holdCounter和state

//    ReentrantReadWriteLock
    protected final boolean tryReleaseShared(int unused) {
        Thread current = Thread.currentThread();
        if (firstReader == current) {
            //如果当前线程是firstReader,firstReaderHoldCount减一,更新firstReader
            if (firstReaderHoldCount == 1)
                firstReader = null;
            else
                firstReaderHoldCount--;
        } else {//更新ThreadLocalHoldCounter
            ReentrantReadWriteLock.Sync.HoldCounter rh = cachedHoldCounter;
            if (rh == null || rh.tid != getThreadId(current))
                rh = readHolds.get();
            int count = rh.count;
            if (count <= 1) {
                readHolds.remove();
                if (count <= 0)
                    throw unmatchedUnlockException();
            }
            --rh.count;
        }
        for (;;) {//循环cas更新state
            int c = getState();
            int nextc = c - SHARED_UNIT;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }

AbstractQueuedSynchronizer.doReleaseShared:队列节点处理

//    AbstractQueuedSynchronizer
    private void doReleaseShared() {
        for (;;) {
            AbstractQueuedSynchronizer.Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == AbstractQueuedSynchronizer.Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, AbstractQueuedSynchronizer.Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                        !compareAndSetWaitStatus(h, 0, AbstractQueuedSynchronizer.Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

释放比较简单:更新HoldCounter,更新队列node。

写锁的上锁和释放锁和ReentrantLock是一样的。

返回顶部

猜你喜欢

转载自www.cnblogs.com/yanhui007/p/12587270.html