JAVA并发编程:阻塞队列-LinkedBlockingQueue

生活

程序员的美德:懒惰,没有耐心以及老子天下第一。

LinkedBlockingQueue成员

首先来看下LinkedBlockingQueue的成员:

 //Integer.MAX_VALUE  默认容量 Integer.MAX_VALUE,可以认为是无界的
    private final int capacity;

   //队列中元素的数量
    private final AtomicInteger count = new AtomicInteger(0);
    //头结点
    private transient Node<E> head;
    //尾节点
    private transient Node<E> last;
    //出队锁
    private final ReentrantLock takeLock = new ReentrantLock();
    //非空条件变量
    private final Condition notEmpty = takeLock.newCondition();
    //入队锁
    private final ReentrantLock putLock = new ReentrantLock();
    //非满条件变量
   private final Condition notFull = putLock.newCondition();

LinkeBlockingQueue可以看做是一个无界(其实是有界的,最大容量的Integer.MAX_VALUE)的链表结构的阻塞队列,且持有双锁,读写各一锁,也就是生产者和消费者各由一把锁来控制同步,也就是意味着在高并发的情况下生产者和消费者可以并行操作队列中的数据,可以提高队列的并发性能。

LinkeBlockingQueue 的 Node结构了解一下:

static class Node<E> {
        E item;

       Node<E> next;

        Node(E x) { item = x; }
    }

LinkedBlockingQueue 创建


    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

   public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

   public LinkedBlockingQueue(Collection<? extends E> c) {
        this(Integer.MAX_VALUE);
        final ReentrantLock putLock = this.putLock;
        putLock.lock(); // Never contended, but necessary for visibility
        try {
            int n = 0;
            for (E e : c) {
                if (e == null)
                    throw new NullPointerException();
                if (n == capacity)
                    throw new IllegalStateException("Queue full");
                enqueue(new Node<E>(e));
                ++n;
            }
            count.set(n);
        } finally {
            putLock.unlock();
        }
    }

LinkedBlockingQueue之生产

不阻塞生产:

//如果队列满了就返回false
    public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        final AtomicInteger count = this.count;
        if (count.get() == capacity)
            return false;
        int c = -1;
        Node<E> node = new Node(e);
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
        //如果容量没上限,入队
            if (count.get() < capacity) {
                enqueue(node);
                c = count.getAndIncrement();
		//如果没到容量上限
                if (c + 1 < capacity)
                //唤醒下一个等待入队的线程
                    notFull.signal();
            }
        } finally {
            putLock.unlock();
        }
        //如果之前队列是空的,那现在入队成功 了就要唤醒等待出队的线程
        if (c == 0)
            signalNotEmpty();
        return c >= 0;
    }

阻塞式生产:

//阻塞 直到入队成功、或者超时、或者被中断
public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {

        if (e == null) throw new NullPointerException();
        long nanos = unit.toNanos(timeout);
        int c = -1;
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            while (count.get() == capacity) {
                if (nanos <= 0)
                    return false;
                nanos = notFull.awaitNanos(nanos);
            }
            enqueue(new Node<E>(e));
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
        return true;
    }

// 阻塞式入队
    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        int c = -1;
        Node<E> node = new Node(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
        //如果队列满了就等待
           while (count.get() == capacity) {
                notFull.await();
            }
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
    }

下面来看下入队的核心代码:

private void enqueue(Node<E> node) {
        // assert putLock.isHeldByCurrentThread();
        // assert last.next == null;
        //设置当前最后一个节点的next节点为新节点,把新节点设置为最后一个节点
        last = last.next = node;
    }

LinkedBlockingQueue之消费

非阻塞式消费:

//队列空,就返回null
public E poll() {
        final AtomicInteger count = this.count;
        if (count.get() == 0)
            return 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;
    }

非阻塞式消费:

//阻塞,直到出队或者中断或者超时
    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        E x = null;
        int c = -1;
        long nanos = unit.toNanos(timeout);
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

//阻塞直到出队或者中断
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();
        // assert head.item == null;
          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;
    }

LindedBlockingDeque

LinkedBlockingDeque就是一个双端队列,任何一端都可以进行元素的出入。代码差不多,就是多个很多first/last结尾的方法,具体可以自己看下,了解下。

ConcurrentLinkedBlockingQueue

这是一个在多线程下可体现高并发性能的链表阻塞队列,无非就是把LinkedBlockingQueue的互斥锁,替换成CAS罢了,原理差不多,都很简单。

猜你喜欢

转载自blog.csdn.net/qq_28605513/article/details/84668493