Java-AbstractQueuedSynchronizer

来源:互联网 发布:php上传文件 身份 编辑:程序博客网 时间:2024/05/20 06:30

Java 中的 ReentrantLock Semaphore ReentrantReadWriteLock等 这些同步的基础都是依靠AbstractQueuedSynchronizer的类来实现。为了方便起见下面使用AQS代替AbstractQueuedSynchronizer。

从ReentrantLock看AQS:
对于ReentrantLock 通常使用的如下:

    reentrantLock.lock();    //to do something    reentrantLock.unlock();

它可以保证to do something 这段代码在同一个时间有且只有一个线程执行这断代码,其余的现场将被挂起,直到获得锁。

reentrantLock.lock()的实现:

  public void lock() {         sync.lock();     }

ReentrantLock内部有一个代理类来具体的完成这个操作,ReentrantLock主要分为公平锁,和非公平锁,而这个的具体实现就是拓展了AQS

       abstract static class Sync extends AbstractQueuedSynchronizer {...}       static final class NonfairSync extends Sync {...}       static final class FairSync extends Sync {...}

公平锁:类似于排队一样,先到先服务的原则
非公平锁:绕过排队直接插入,当然也有插入不进去的:)

公平锁的获取:

      final void lock() {                  acquire(1);              }

其中acquire 方法为AQS的 acquire方法

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

这代码就是尝试的获取锁,如果获取失败,将这个请求加入的到获取锁的队列之中

tryAcquire方法在AQS类中么有给出具体的实现,显然是交给子类自己去实现:

    protected boolean tryAcquire(int arg) {            throw new UnsupportedOperationException();        }

那就来看一下他的子类公平锁的具体实现:

        protected final boolean tryAcquire(int acquires) {                    final Thread current = Thread.currentThread();                    int c = getState();                    if (c == 0) {                        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;                }

方法getState 的实现是在AQS中,使用一个volatile变了来表示一个锁的使用状态

        private volatile int state;        protected final int getState() {            return state;        }

如果 state == 0
表示锁没有被其他线程使用然后在调用hasQueuedPredecessors()方法判断队列是否还有其他线程,如果没有没有其他线程,说明没有其他线程正在占有锁,那就进行获取锁,将state 设置为获取状态,如果通过CAS(compareAndSetState)操作将状态为更新成功则代表当前线程获取锁,因此,将当前线程设置到AQS的一个变量中,说明这个线程拿走了锁。

如果不为0 意味着,锁已经被拿走了,但是,因为ReentrantLock是重入锁,是可以重复lock,unlock的,只要成对出现行。一次。这里还要再判断一次 获取锁的线程是不是当前请求锁的线程。如果是的,累加在state字段上就可以了。致此,如果获取到锁,那就返回true ,如果么有获取到,那就返回false,然后将获取的锁的线程放入到队列中,只是在放入到队列之前要进行包装一下:

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

使用当前线程通过设置mode(独占,共享)构造一个node节点,创建这个节点加入到队列的尾部,如果队列不为空队列,那么通过CAS操作将当前节点设置为最新想获取锁的节点,如果失败则将进入一个死循环进行更改。将获取锁的节点加入到队列还需要将当前线程挂起

      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;                    }                    if (shouldParkAfterFailedAcquire(p, node) &&                        parkAndCheckInterrupt())                        interrupted = true;                }            } finally {                if (failed)                    cancelAcquire(node);            }        }

if (p == head && tryAcquire(arg)) 如果当前的节点是head说明他是队列中第一个“有效的”节点,因此尝试获取,上文中有提到这个类是交给子类去扩展的, shouldParkAfterFailedAcquire(p, node) 否则,检查前一个节点的状态为,看当前获取锁失败的线程是否需要挂起。/如果需要,借助JUC包下的LockSopport类的静态方法Park挂起当前线程。知道被唤醒。

获取锁,说完了,释放锁也有异曲同工之妙,和获取锁的相应对应,获取一个锁,标示为+1,释放一个锁,标志位-1。具体的释放过程也只子类自己去实现:

    protected final boolean tryRelease(int releases) {                int c = getState() - releases;                if (Thread.currentThread() != getExclusiveOwnerThread()) //如果释放的线程和获取锁的线程不是同一个,抛出非法监视器状态异常。                    throw new IllegalMonitorStateException();                boolean free = false;                if (c == 0) {//因为是重入的关系,不是每次释放锁c都等于0,直到最后一次释放锁时,才通知AQS不需要再记录哪个线程正在获取锁。                    free = true;                    setExclusiveOwnerThread(null);                }                setState(c);                return free;            }

释放锁成功过后,找到AQS头节点,并且进行唤醒线程,寻找的顺序是从队列尾部开始往前去找的最前面的一个waitStatus小于0的节点

  private void unparkSuccessor(Node node) {        int ws = node.waitStatus;        if (ws < 0)            compareAndSetWaitStatus(node, ws, 0);        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)            LockSupport.unpark(s.thread);    }

公平锁,获取和释放都说完了,那非公平的获取和释放那就很好理解了,他们之间的区别就在于非公平的锁,在获取的时候进行更改,如果更改不成功那还是要入队列的

        final void lock() {                   if (compareAndSetState(0, 1))                       setExclusiveOwnerThread(Thread.currentThread());                   else                       acquire(1);               }

AQS只是维护一个状态,一个控制各个线程何时可以访问的状态,它只对状态负责,而这个状态表示什么含义,由子类自己去定义。

0 0
原创粉丝点击