10 多线程与高并发 - JUC 阻塞队列(ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue)源码解析

ArrayBlockingQueue

ArrayBlockingQueue 底层是采用数组实现的一个队列。一般称为有界队列,其阻塞方式是基于 ReentrantLock 实现的,线程先在等待队列里面,signal() 将等待队列的首节点加入到阻塞队列的队尾,unlock() 后开始唤醒线程。

常用方法

  • 存数据

    • add(E e):添加数据到队列,满了抛异常
    • offer(E e):添加数据到队列,满了返回false
    • put(E e):添加数据到队列,满了线程挂起
    • offer(E e, long timeout, TimeUnit unit):添加数据到队列,满了线程挂起一段时间
  • 取数据

    • remove():从队列拿数据,拿到返回,拿到null,抛异常
    • poll():从队列拿数据,拿到返回,拿到null,也返回
    • take():从队列拿数据,拿到返回,没拿到一直阻塞
    • poll(long timeout, TimeUnit unit):从队列拿数据,拿到返回,没拿到阻塞一段时间

核心属性

 	/** 数组 */
    final Object[] items;

    /** 下次取数索引 */
    int takeIndex;

    /** 下次存放数据索引 */
    int putIndex;

    /** 数组长度 */
    int count;

    /** ReentrantLock 锁 */
    final ReentrantLock lock;

    /** 取值等待队列 */
    private final Condition notEmpty;

    /** 存值等待队列 */
    private final Condition notFull;

源码解析

存数据

// 添加成功返回ture
// 添加失败抛异常
public boolean add(E e) {
        if (offer(e))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }

// 添加成功返回ture
// 添加失败返回false
public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
            	// 存放数据到数组中
                enqueue(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

// 数组满了挂起线程
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();
        }
    }

// 数组满了挂起一段时间
public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {

        checkNotNull(e);
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length) {
                if (nanos <= 0)
                    return false;
                nanos = notFull.awaitNanos(nanos);
            }
            enqueue(e);
            return true;
        } finally {
            lock.unlock();
        }
    }

重点看 enqueue() 即可

// 存放数据到数组中
private void enqueue(E x) {
		// 拿到数组
        final Object[] items = this.items;
        // 在数组 putIndex 位置插入数据
        items[putIndex] = x;
        // 存放数据索引到最后一个位置就置为 0
        if (++putIndex == items.length)
            putIndex = 0;
        // 数组长度 ++ 
        count++;
        // 唤醒取值的阻塞线程
        notEmpty.signal();
    }

取数据

// 没拿到数据抛异常
public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

// 拿到null也返回
public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : dequeue();
        } finally {
            lock.unlock();
        }
    }

// 从队列拿数据,拿到返回,没拿到一直阻塞
public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

// 从队列拿数据,拿到返回,没拿到阻塞一段时间
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0) {
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

重点看 dequeue() 即可

private E dequeue() {
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        // 取数
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        // 取数索引到数组长度置为 0
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        // 迭代器相关操作
        if (itrs != null)
            itrs.elementDequeued();
        // 唤醒存值的阻塞线程
        notFull.signal();
        return x;
    }

LinkedBlockingQueue

底层基于 Node 封装的单向链表实现,会将每个元素封装为 Node(LinkedBlockingQueue 的内部类)结点,Node 有当前值,next指针,一般称为无界队列。内部提供了读锁和写锁,读写不互斥。

核心属性

// 阻塞队列元素封装为Node
static class Node<E> {
        E item;
        Node<E> next;
        Node(E x) { item = x; }
    }

    // 队列长度 不传为 Integer.MAX
    private final int capacity;

    // 数据条数
    private final AtomicInteger count = new AtomicInteger();

    // 伪头结点 isNULL 
    transient Node<E> head;

    // 伪尾结点 isNULL 
    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();

	// 构造方法,将 last 与 head 给定 null
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

源码解析

存数据

// 添加成功返回ture
// 添加失败抛异常
 public boolean add(E e) {
        if (offer(e))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }

// 添加到队列,满了返回false
public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        final AtomicInteger count = this.count;
        // 队列满了返回false
        if (count.get() == capacity)
            return false;
        int c = -1;
        // 包装成 Node 结点
        Node<E> node = new Node<E>(e);
        // 写锁
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            if (count.get() < capacity) {
            	// 插入数据
                enqueue(node);
                // count ++ 
                c = count.getAndIncrement();
                // 添加完数据之后,长度依然小于最大长度,唤醒可能阻塞的写线程 
                // 读写不互斥,可能前面在执行时,队列是满的,但是读操作依然在进行
                if (c + 1 < capacity)
                    notFull.signal();
            }
        } finally {
            putLock.unlock();
        }
        // c == 0,说明添加数据之前,队列是空的,唤醒可能阻塞的读线程
        if (c == 0)
            signalNotEmpty();
        return c >= 0;
    }


public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        int c = -1;
        Node<E> node = new Node<E>(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();
        }
        // c == 0,说明添加数据之前,队列是空的,唤醒可能阻塞的读线程
        if (c == 0)
            signalNotEmpty();
    }


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();
        }
        // c == 0,说明添加数据之前,队列是空的,唤醒可能阻塞的读线程
        if (c == 0)
            signalNotEmpty();
        return true;
    }

重点看 enqueue() 即可

// 往尾部插入数据
private void enqueue(Node<E> node) {
	last = last.next = node;
   }

取数据

// 没拿到数据抛异常
public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }
// 
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 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;
    }



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

重点看 enqueue() 即可

// 从头开始取,first 指向 第二个值
private E dequeue() {
        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;
    }

上面的 await() 与 signal() 调用了 AQS 中 ConditionObject 类的方法,这里不在赘述

ConditionObject 介绍

SynchronousQueue

SynchronousQueue 传递数据,并不存储数据,队列的长度是 0 ,一个线程将数据丢到 SynchronousQueue 后,必须等另一个线程拿到这个数据,两者配对成功才可以。可作为线程间的通讯。

简单使用

public static void main(String[] args) throws InterruptedException {
    SynchronousQueue queue = new SynchronousQueue();

    new  Thread(() -> {
        User user = new User();
        user.setName("jack&rose");
        System.out.println("publisher:" + user);
        queue.offer(user);
    }).start();
  
    new Thread(() -> {
        try {
            Object user = queue.take();
            System.out.println("consumer:" + user);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
}

核心属性

SynchronousQueue 无论是消费者还是生产者,都用到了Transferer中的transfer方法

  • 生产者调用transfer方法,需要传递一个参数,也就是数据
public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        if (transferer.transfer(e, false, 0) == null) {
            Thread.interrupted();
            throw new InterruptedException();
        }
    }
  • 消费者调用transfer方法,第一个参数传递为null,代表获取数据
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        E e = transferer.transfer(null, true, unit.toNanos(timeout));
        if (e != null || !Thread.interrupted())
            return e;
        throw new InterruptedException();
    }
/**
构造函数
TransferQueue代表公平处理方式
TransferStack代表不公平处理方式
*/
public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }


//这个是 TransferQueue 中的每一个节点
// 队列可全是消费者或全是生产者
static final class QNode {
    volatile QNode next;   
    volatile Object item;   
    // 如果执行了put、take方法时,需要挂起线程,而挂起的就是这个线程
    volatile Thread waiter;   
    // true:生产者
    // false:消费者
    final boolean isData;
}   
/** Head of queue */
transient volatile QNode head;
/** Tail of queue */
transient volatile QNode tail;


// 无参构造
TransferQueue() {
    // 会先初始化一个QNode,作为head和tail的指向,并且这个QNode不包含线程信息,就一个伪的头结点
    QNode h = new QNode(null, false); // initialize to dummy node.
    head = h;
    tail = h;
}

源码分析

因为存数据、取数据都是调用 transfer() 方法,所以这里看下 TransferQueue.transfer 方法

E transfer(E e, boolean timed, long nanos) {
            // 声明QNode
            QNode s = null; 
			// 判断当前操作的是消费者还是生产者 
			// true:生产者 
			// false:消费者
            boolean isData = (e != null);

            for (;;) {
				// 头尾节点
                QNode t = tail;
                QNode h = head;
                if (t == null || h == null)         // saw uninitialized value
                    continue;                       // spin
				
				// 如果头和尾相等。 
				// 在当前的QNode单向链表中,要么都存放生产者,要么都存放消费者。 
				// 所以第二个判断是,如果队列中有Qnode,查看我当前的isData是否和队列中Qnode的isData一致,一致挂上去
                if (h == t || t.isData == isData) { // empty or same-mode
					
                    QNode tn = t.next;
                    if (t != tail)                  // inconsistent read
                        continue;
					// 如果尾节点的next不为null,有并发情况 
                    if (tn != null) {               // lagging tail
						// 直接CAS操作,将tail的next节点设置为tail节点
                        advanceTail(t, tn);
                        continue;
                    }
					// timed == true: offer,poll 
					// 进来之后没有立即配对,那就直接告辞!
                    if (timed && nanos <= 0)        // can't wait
                        return null;
					// 初始化节点
                    if (s == null)
                        s = new QNode(e, isData);
					// 将tail的next指向的当前QNode
                    if (!t.casNext(null, s))        // failed to link in
                        continue;
					// 将tail指向当前Qnode
                    advanceTail(t, s);              // swing tail and wait
					// 挂起线程直到被唤醒拿到指定数据
                    Object x = awaitFulfill(s, e, timed, nanos);
					// 拿到的数据和当前QNode一致,当前节点取消了
                    if (x == s) {                   // wait was cancelled
						// 清除当前节点
                        clean(t, s);
                        return null;
                    }
					// 判断是否还在队列中
                    if (!s.isOffList()) {           // not already unlinked
						// 将当前节点设置为新的head
                        advanceHead(t, s);          // unlink if head
                        if (x != null)              // and forget fields
							// 我拿到数据了,设置item为当前节点对象
                            s.item = s;
						// 线程置位null
                        s.waiter = null;
                    }
					// 返回数据
                    return (x != null) ? (E)x : e;

                } else {                            // complementary-mode
					// 需要配对的数据
                    QNode m = h.next;               // node to fulfill
					// 并发问题,重新循环
                    if (t != tail || m == null || h != head)
                        continue;                   // inconsistent read
					// 拿到m中的数据。 
					// x == null:队列是消费者 
					// x != null:队列是生产者
                    Object x = m.item;
					// 1、出现并发问题
					// 2、取出的数据,竟然是节点本身,代表节点被取消! 
					// 3、开始交换数据,将当前方法传入数据,替换到head的next,如果操作失败,并发问题
                    if (isData == (x != null) ||    // m already fulfilled
                        x == m ||                   // m cancelled
                        !m.casItem(x, e)) {         // lost CAS
                        advanceHead(h, m);          // dequeue and retry
                        continue;
                    }
					// 操作成功,也要替换head
                    advanceHead(h, m);              // successfully fulfilled
					// 唤醒队列中的head的next节点的线程。
                    LockSupport.unpark(m.waiter);
					// 操作成功,返回数据
                    return (x != null) ? (E)x : e;
                }
            }
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小刘说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值