ReentrantLock和AQS

demo

public static void main(String[]args){
		final ReentrantLock lock = new ReentrantLock();
		for(int j=0;j<5;j++){
			new Thread(new Runnable(){
				public void run(){
					try{
					   //加锁
						lock.lock();  
						for (int i = 0; i < 5; i++) {
							System.out.println(Thread.currentThread().getName()+" : "+i);
						}
					}catch(Exception e){
						e.printStackTrace();
					}finally{
					   //释放锁
						lock.unlock();
					}
				}
			}).start();
		}
	}

lock.lock(); 等同synchronized,
lock.unlock(); 等同synchronized的方法块结束

public class ReentrantLock implements Lock, java.io.Serializable {
 private final Sync sync;
    
    //默认是非公平锁
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    
    /**
    *下面是经常用到的2个方法,lock 和 unlock
    *都用到AQS里的方法
    **/
    
    
    //!!!最重要的方法-lock
    public void lock() {
        sync.lock();
    }
    
    //!!!最重要的方法-unlock
    public void unlock() {
        sync.release(1);
    }
    

    //!!!重点 AbstractQueuedSynchronizer 简称为AQS 
    //ReentrantLock里的抽象类Sync实现AbstractQueuedSynchronizer
    //抽象类Sync又衍生出2个实现类,公平锁和非公平锁
    abstract static class Sync extends AbstractQueuedSynchronizer {
        //留了一个lock的抽象方法
        abstract void lock();
    
        //默认实现了非公平锁的获取
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //volatile实现了可见性
            int c = getState();
            if (c == 0) {
                //unsafe里的cas写,区别于公平锁的地方
                //队列里的线程和这里的线程会共同去竞争锁
                //这对于排队的线程是不公平的
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
    
    //公平锁
     static final class FairSync extends Sync {
        final void lock() {
            acquire(1);
        }
      protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
               //公平锁区分非公平锁就在!hasQueuedPredecessors()上
               //如果判断有线程在队列里排队,当前请求线程乖乖去排队吧
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

    }
    
   //非公平锁  
     static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    

下面列出了公平锁和非公平锁的区别

公平锁非公平锁
lock()一进来就参与竞争锁不参与竞争锁
tryAcquire()不判断是否有线程在排队等待获取锁判断是否有线程在排队等待获取锁

ReentrantLock默认使用非公平锁

  static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        
        
        final void lock() {
           //cas 更新AQS里的state 从 0->1 
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
            //cas 更新不成功,调用AQS里的acquire
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

AbstractQueuedSynchronizer(AQS)里的acquire

    public final void acquire(int arg) {
        //tryAcquire先获取一次,
        //如果失败则调用acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
        //addWaiter(Node.EXCLUSIVE)是将当前的线程封装成为一个node,放到等待队列的末尾
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

addWaiter(Node.EXCLUSIVE)

 private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
       //如果尾节点不为空的话,就将自己添加到链表的尾部
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //如果上面那步不成功的话,就调用enq(node)
        //其实里面的逻辑和上面差不多,
        //多了尾节点为空判断
        //然后循环上面的过程直到加入成功
        enq(node);
        return node;
    }

AQS里的队列结构,上面新加的节点将会放到下面双向链表的结尾,
加入成功后,调用acquireQueued去竞争锁

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                //在循环中尝试去将AQS里的state将0->1
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //如果尝试2次获取失败,则会调用lockSupport的挂起操作,
                //挂起线程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

挂起的线程等待lock.unlock()唤醒,unlock调用sync.release(1)

 public final boolean release(int arg) {
    //将AQS里的state -1,如果state减到0
        if (tryRelease(arg)) {
            Node h = head;
            //去唤醒队列中的后继的那个节点
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

tryRelease是个抽象方法,ReentrantLock的syn内部类里实现

protected final boolean tryRelease(int releases) {
//将AQS里的state -1
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

unparkSuccessor

private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         上面的解释是如果后续节点是正常的,即node.waiteStauts=0,
         那就唤醒这个节点,如果node.waiteStauts>0,就是这个thread被cancel了,
         比如lock.tryAcquireNanos(int arg, long nanosTimeout),如果获取锁超时
         则表示这个node标记为cancel,
         如果节点是cancel,倒序寻找到未被标记为cancelled的节点
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
           //唤醒节点,唤醒的节点重新进入竞争锁(将state 0->1)
            LockSupport.unpark(s.thread);
    }

简单的描述上面的过程,如果多个线程同时去 调用lock.lock() 即将AQS里的state从0改成1,这个更改是通过CAS 来更改的。也就是只有一个线程会更改成功,更改成功的这个线程就获得了锁。其它线程则会排队,
排完队后,线程会去尝试获取锁,尝试2次,如果失败了,则线程挂起。 等待lock.unlock()来唤醒。


重新复习了下AQS. 对AQS里的锁的机制又有个新的认识。
该图来之这位的博客
在这里插入图片描述

将node连在一起形成了单链表不是特别好,事实上代码里并没有形成单链表的数据结构。逻辑上可以这么认为。


public class CLHLockDemo {

    public static void main(String[] args) {
       
        CLHLock lock =new CLHLock();
        for(int i=0;i<10;i++){
            new Thread(()->{
                lock.lock(); //加锁
                System.out.println(Thread.currentThread().getName()+" is working ");
                try {
                    sleep(1000);
                    System.out.println(Thread.currentThread().getName()+" end working ");
                    lock.unlock(); //解锁
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
    static class CLHLock{
        private  ThreadLocal<CLHNode> curNodeInThread;
        private AtomicReference<CLHNode> tail;
        public CLHLock(){
            curNodeInThread=new ThreadLocal();
            tail=new AtomicReference();
        }
        public void lock(){
            CLHNode curNode=new CLHNode();
            curNodeInThread.set(curNode);
            CLHNode preNode=tail.getAndSet(curNode);
            if(preNode!=null){
                while(preNode.isWork()){
                    //spin here
                }
            }
        }
        public void unlock(){
            CLHNode curNode=curNodeInThread.get();
            curNodeInThread.remove();
            if(curNode!=null&&curNode.isWork()){
                curNode.setWork(false);
            }
        }
    }
    static class CLHNode{
        private volatile boolean work=true;

        public boolean isWork() {
            return work;
        }

        public void setWork(boolean work) {
            this.work = work;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值