BlockingQueue学习笔记

1. BlockingQueue接口的常用实现类如下:

BlockingQueue学习笔记



BlockingQueue定义的常用方法如下:

     抛出异常      特殊值        阻塞       超时

插入  add(e)       offer(e)     put(e)     offer(e, time, unit)
移除  remove()     poll()       take()     poll(time, unit)
检查  element()    peek()       不可用     不可用


解释:

add:   如果当前队列已满,执行add操作将抛出异常

offer: 如果当前队列已满,执行offer操作将返回false(JDK6)

put:   如果当前队列已满,则无限期阻塞下去直到队列可插入

take:  如果当前队列为空,则无限期阻塞下去直到队列非空


2. LinkedBlockingQueue

实例化代码:

public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);//创建一个Node,令head和last都指向这位对象
    }

从实例化源码可以看出LinkedBlockingQueue是用链表结构实现的,实例化时并不分配所有空间


 add():

 public boolean add(E e) {
        if (offer(e))//add操作实际是调用了offer
            return true;
        else
            throw new IllegalStateException("Queue full");//如果队列已满则抛出异常
    }


 offer():

public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        final AtomicInteger count = this.count;
        if (count.get() == capacity)//队列已满,链表长度等于初始化时的capacity
            return false;
        int c = -1;
        final ReentrantLock putLock = this.putLock;
        putLock.lock();//对enqueue操作加锁
        try {
            if (count.get() < capacity) {
                enqueue(e);
                c = count.getAndIncrement();
                if (c + 1 < capacity)
                    notFull.signal();//唤醒读线程
            }
        } finally {
            putLock.unlock();//ReentrantLock一定要在finally中释放锁
        }
        if (c == 0)
            signalNotEmpty();
        return c >= 0;
    }
    可见队列满的情况下仅仅返回false


 put()

public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        // Note: convention in all put/take/etc is to preset local var
        // holding count negative to indicate failure unless set.
        int c = -1;
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            /*
             * Note that count is used in wait guard even though it is
             * not protected by lock. This works because count can
             * only decrease at this point (all other puts are shut
             * out by lock), and we (or some other waiting put) are
             * signalled if it ever changes from
             * capacity. Similarly for all other uses of count in
             * other wait guards.
             */
            while (count.get() == capacity) { 
                    notFull.await();//如果队列已满,则阻塞写线程
            }
            enqueue(e);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();//唤醒写线程
        } finally {
            putLock.unlock();//释放锁
        }
        if (c == 0)
            signalNotEmpty();
    }
    
    //插到队尾
     private void enqueue(E x) {
        // assert putLock.isHeldByCurrentThread();
        last = last.next = new Node<E>(x);
    }


take():

public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
                while (count.get() == 0) {
                    notEmpty.await();
                }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }
    
    
    //将队列头的元素返回
    private E dequeue() {
        // assert takeLock.isHeldByCurrentThread();
        Node<E> h = head;
        Node<E> first = h.next;
        h.next = h; // help GC
        head = first;
        E x = first.item;
        first.item = null;
        return x;
    }


poll():

public E poll() {
        final AtomicInteger count = this.count;
        if (count.get() == 0)
            return null;//如果队列空则返回null
        E x = null;
        int c = -1;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            if (count.get() > 0) {
                x = dequeue();//队列非空,获取头元素
                c = count.getAndDecrement();
                if (c > 1)
                    notEmpty.signal();//唤醒写线程
            }
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }



3. ArrayBlockingQueue

实例化代码:

public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        //实例化的时候就分配一个数组空间 注意与LinkedBlockingQueue的区别
        this.items = (E[]) new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }


add():

//同LinkedBlockingQueue的add
public boolean add(E e) {
	return super.add(e);
    }


offer():

public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;//数组已满,返回false
            else {
                insert(e);//插入数组
                return true;
            }
        } finally {
            lock.unlock();
        }
    }
    
    
    private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex);//索引加1
        ++count;
        notEmpty.signal();//唤醒写线程
    }


4. SynchronousQueue

同步Queue,属于线程安全的BlockingQueue的一种,此队列设计的理念类似于"单工模式",对于每个put/offer操作,必须等待一个take/poll操作,类似于我们的现实生活中的"火把传递":一个火把传递地他人,需要2个人"触手可及"才行. 因为这种策略,最终导致队列中并没有一个真正的元素;这是一种pipleline思路的基于queue的"操作传递".

  •  void put(E o):向队列提交一个元素,阻塞直到其他线程take或者poll此元素.

  • boolean offer(E o):向队列中提交一个元素,如果此时有其他线程正在被take阻塞(即其他线程已准备接收)或者"碰巧"有poll操作,那么将返回true,否则返回false.

  •   E take():获取并删除一个元素,阻塞直到有其他线程offer/put.

  •  boolean poll():获取并删除一个元素,如果此时有其他线程正在被put阻塞(即其他线程提交元素正等待被接收)或者"碰巧"有offer操作,那么将返回true,否则返回false.

  •  E peek():总会返回null,硬编码.(转)


你可能感兴趣的:(BlockingQueue学习笔记)