AbstractQueueSynchronizer

AbstractQueueSynchronizer

AbstractQueueSynchronizer 是基于 FIFO线程等待队列 的一个同步器开发框架。

这篇文章首先介绍同步器概念,然后介绍AQS的结构原理

什么是Synchronizer(同步器)

并发环境下,Synchronizer用于实现线程之间的协同。具体而言,就是哪个线程应该阻塞,哪个线程应该被唤醒。

常见的同步器实现有:CountdownLatchCycleBarrierReentrantLock等…

使用CountdownLatch进行协同的一个例子:

这里的一个例子是服务初始化,n个initializer并发执行,init方法内调用latch.await()等待所有initializer执行完成。

package org.otaku.example;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;

public class ServiceInit {
    private static final AtomicInteger ID_GENERATOR = new AtomicInteger(0);
    private final CountDownLatch latch;
    private final int count;

    public ServiceInit(int count) {
        latch = new CountDownLatch(count);
        this.count = count;
    }

    public void init() throws InterruptedException {
        for (int i = 0; i < count; i++) {
            new Initializer().start();
        }
        latch.await();
        System.out.println("all initializers finished!");
    }

    class Initializer extends Thread {
        private final int id = ID_GENERATOR.incrementAndGet();

        public void run() {
            try {
                Thread.sleep(ThreadLocalRandom.current().nextLong(1000, 3000));
            } catch (InterruptedException ignored) {
            }
            System.out.println("initializer-" + id + " finished!");
            latch.countDown();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        ServiceInit serviceInit = new ServiceInit(5);
        serviceInit.init();
    }

}

AQS架构

AQS包含以下元素:

  • int类型的state字段存储状态
  • FIFO队列,队内元素是阻塞中的线程,这些线程在队列中等待被唤醒(当状态被更新)
  • 改变状态的方法,根据状态的改变,线程被阻塞入队,或者被唤醒出队

acquire

acquire方法用于申请互斥资源。

AbstractQueueSynchronizeracquire方法调用子类实现的tryAquire方法:

	//申请互斥资源
    public final void acquire(int arg) {
    	//调用tryAquire,如果返回true,则线程通过
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //如果tryAquire返回false,则线程入队阻塞
            selfInterrupt();
    }

tryAquire留给子类实现:

//返回true,线程通过;返回false,线程入队阻塞
//调用acquire,tryAquire至少被调用一次,若返回false则入队阻塞,后续被唤醒后
//将继续尝试调用tryAquire,直到成功为止
protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

调用acquire流程如下:
AbstractQueueSynchronizer_第1张图片

release

release方法用于释放互斥资源
AbstractQueueSynchronizerrelease方法调用子类实现的tryRelease方法:

	//释放互斥资源
    public final boolean release(int arg) {
    	//调用tryRelease,返回true,唤醒队头线程;返回false则方法执行结束
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

tryRelease留给子类实现:

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

一个简单的锁实现

Doug Lea在JDK文档中给出了使用AQS的一个例子:简单的不支持重入的互斥锁:

class Mutex implements Lock, java.io.Serializable {

   // Our internal helper class
   private static class Sync extends AbstractQueuedSynchronizer {
     // Acquires the lock if state is zero
     public boolean tryAcquire(int acquires) {
       assert acquires == 1; // Otherwise unused
       //CAS获取锁,从0到1则获取成功
       if (compareAndSetState(0, 1)) {
       	 //记录获取锁的线程
         setExclusiveOwnerThread(Thread.currentThread());
         //返回true,线程通过
         return true;
       }
       //获取锁失败,返回false,线程入队阻塞
       return false;
     }

     // Releases the lock by setting state to zero
     protected boolean tryRelease(int releases) {
       assert releases == 1; // Otherwise unused
       if (!isHeldExclusively())
         throw new IllegalMonitorStateException();
       setExclusiveOwnerThread(null);
       //将状态设置为0
       setState(0);
       //返回true,则唤醒队头阻塞线程,重新尝试tryAcquire
       return true;
     }

     // Reports whether in locked state
     public boolean isLocked() {
       return getState() != 0;
     }

     public boolean isHeldExclusively() {
       // a data race, but safe due to out-of-thin-air guarantees
       return getExclusiveOwnerThread() == Thread.currentThread();
     }

     // Provides a Condition
     public Condition newCondition() {
       return new ConditionObject();
     }

     // Deserializes properly
     private void readObject(ObjectInputStream s)
         throws IOException, ClassNotFoundException {
       s.defaultReadObject();
       setState(0); // reset to unlocked state
     }
   }

   // The sync object does all the hard work. We just forward to it.
   private final Sync sync = new Sync();

   public void lock()              { sync.acquire(1); }
   public boolean tryLock()        { return sync.tryAcquire(1); }
   public void unlock()            { sync.release(1); }
   public Condition newCondition() { return sync.newCondition(); }
   public boolean isLocked()       { return sync.isLocked(); }
   public boolean isHeldByCurrentThread() {
     return sync.isHeldExclusively();
   }
   public boolean hasQueuedThreads() {
     return sync.hasQueuedThreads();
   }
   public void lockInterruptibly() throws InterruptedException {
     sync.acquireInterruptibly(1);
   }
   public boolean tryLock(long timeout, TimeUnit unit)
       throws InterruptedException {
     return sync.tryAcquireNanos(1, unit.toNanos(timeout));
   }
 }

acquireShared

acquireShared方法用于申请共享资源

    public final void acquireShared(int arg) {
    	//1.调用tryAcquireShared,>= 0通过,< 0则进入线程等待队列
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

tryAcquireShared
tryAcquire不同,这里返回一个int,代表申请到的资源个数。返回<0代表资源申请失败;返回0代表申请资源成功,且后续申请都将失败;返回>0代表后续申请可能成功。

protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

releaseShared

releaseShared方法用于释放互斥资源

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
        	//唤醒阻塞线程
            doReleaseShared();
            return true;
        }
        return false;
    }

tryReleaseShared

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

CountdownLatch实现

使用CountdownLatch,多个线程在latch上等待,当latch被打开,多个线程都被放行,因此在latch上等待即为申请共享资源,而调用countdown方法则是尝试释放资源许可。

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
        	//一旦latch被打开,线程将被放行,否则阻塞
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                //门已被打开,返回false,因为没有线程需要被唤醒
                if (c == 0)
                    return false;
                int nextc = c - 1;
                //CAS将状态-1
                if (compareAndSetState(c, nextc))
                	//只有状态减到0,才能释放等待线程,否则release都返回false
                    return nextc == 0;
            }
        }
    }

你可能感兴趣的:(java,并发编程,AQS)