java-并发-源码-BlockingQueue

一.BlockingQueue

ArrayBlockingQueue和LinkedBlockingQueue的区别:

  1. 队列中锁的实现不同
    ArrayBlockingQueue实现的队列中的锁是没有分离的,即生产和消费用的是同一个锁;
    LinkedBlockingQueue实现的队列中的锁是分离的,即生产用的是putLock,消费是takeLock
  2. 在生产或消费时操作不同
    ArrayBlockingQueue实现的队列中在生产和消费的时候,是直接将枚举对象插入或移除的;
    LinkedBlockingQueue实现的队列中在生产和消费的时候,需要把枚举对象转换为Node进行插入或移除,会影响性能
  3. 队列大小初始化方式不同
    ArrayBlockingQueue实现的队列中必须指定队列的大小;
    LinkedBlockingQueue实现的队列中可以不指定队列的大小,但是默认是Integer.MAX_VALUE
可能报异常 返回布尔值 可能阻塞 设定等待时间
入队 add(e) offer(e) put(e) offer(e, timeout, unit)
出队 remove() poll() take() poll(timeout, unit)
查看 element() peek()

add(e) remove() element() 方法不会阻塞线程。
offer(e) poll() peek() 方法即不会阻塞线程,也不会抛出异常。
要想要实现阻塞功能,需要调用put(e) take() 方法。当不满足约束条件时,会阻塞线程。

二.ArrayBlockingQueue

这是一个循环队列,index到结尾了,会从头部开始

    final ReentrantLock lock;
    private final Condition notEmpty;
    private final Condition notFull;
    final Object[] items;
    int takeIndex;
    int putIndex;
    int count;

内部位置一个ReentrantLock和这个锁创建的两个condition
items是创建时指定大小的数组
count是存储了多少个元素
putIndex是队尾指针
takeIndex是队头指针

put代码

    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

put代码,如果满了需要等待在notFull这condition里,等待非满信号
take代码

  public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

如果队列空了,需要等待在notEmpty这个condition里,等待非空信号;

put和take是生产者消费者模式
enqueue(e)是生产对象,填进队列中;生产完之后,会发出非空信号;
dequeue()是消费对象,并且返回;消费完之后,会发出非满信号;

enqueue代码

   private void enqueue(E x) {
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }

dequeue代码

    private E dequeue() {
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

三.ReentrantLock的lockInterruptibly方法

put调用了ReentrantLock得lockInterruptibly方法进行锁定
调用关系lockInterruptibly->sync.acquireInterruptibly(1)代码如下

   public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

如果尝试没有获取到锁,那么执行doAcquireInterruptibly(1),如下

private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

增加一个等待链表,所有尝试获取的线程都会阻塞
park方法如下,调用park应该是会阻塞,如果被中断,Thread.interrupted()应该会返回true,因为抛出中断异常

    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }

四.ReentrantLock的condition和awiait

构造时创建了condition

	notEmpty = lock.newCondition();
	notFull =  lock.newCondition();

AQS中有ConditionObject实现,具有如下成员

    private transient Node firstWaiter;
    private transient Node lastWaiter;

await
代码如下:

   public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter();
        int savedState = fullyRelease(node);
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            LockSupport.park(this);
            ...
        }
        ...
    }

await调用addConditionWaiter创建等待链表,返回最后一个结点
调用fullyRelease释放掉该node占用的Reentranlock
链表节点创建时指定了Node.CONDITION,所以isOnSyncQueue都返回false

   final boolean isOnSyncQueue(Node node) {
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        if (node.next != null) // If has successor, it must be on queue
            return true;
        return findNodeFromTail(node);
    }

因此线程会锁在LockSupport.park(this)的位置

signal

 	public final void signal() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        Node first = firstWaiter;
        if (first != null)
            doSignal(first);
    }

signal调用了doSignal方法,向链表中第一个节点发信号

 	private void doSignal(Node first) {
        do {
            if ( (firstWaiter = first.nextWaiter) == null)
                lastWaiter = null;
            first.nextWaiter = null;
        } while (!transferForSignal(first) &&
                 (first = firstWaiter) != null);
    }

transferForSignal
把节点从condition队列转到sync队列,同时把状态修改为signal,如果有很多线程,会产生一个链表

 	final boolean transferForSignal(Node node) {
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))	//node类型设置为0
            return false;
        Node p = enq(node);	//生成一队node队列
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

这个时候等待线程unpark,继续执行阻塞前的代码

继续await代码

 	public final void await() throws InterruptedException {
	    ...
        while (!isOnSyncQueue(node)) {
            ...
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }

由于进入了同步队列,跳出while循环
acquireQueued使线程形成一个阻塞队列,抢占到cpu的线程恢复state状态

posted @ 2016-09-14 19:34  zhangshihai1232  阅读(217)  评论(0)    收藏  举报