深度剖析阻塞队列的设计原理及实现

阻塞队列在很多地方会使用到,比如线程池,Zookeeper。一般使用阻塞队列来实现生产中和消费者模型。

什么是队列?

队列是一种基本的数据结构,它是一个只允许在一端进行移除操作,在另一边进行插入操作的线性表,允许插入的一端叫队尾,允许移除的一端成为队头。

什么是阻塞队列?

其实阻塞队列在队列的基础上增加了两个操作。

  • 支持阻塞插入:在队列满的情况下,会阻塞继续往队列中添加数据的线程,直到队列中有元素被释放。
  • 支持阻塞移除:在队列为空的情况下,会阻塞从队列中获取元素的线程,直到队列中添加了新的元素。

JAVA中提供的阻塞队列

队列名称功能
ArrayBlockingQueue由数组实现的有界队列,按照FIFO的原则对数组进行排序。
LinkedBlockingQueue由链表实现的有界阻塞队列,默认和最大长度Integer.MAX_VALUE,队列按照先进先出FIFO的原则对数组进行排序。
PriorityBlockingQueue支持优先级排序的有界阻塞队列,默认按自然顺序的升序;也可以支持自定义实现CompareTo()指定元素顺序排序规则;或者在初始化PriorityBlockingQueue时,指定构造参数Comparator来对元素进行排序
DelayQueue由优先级队列实现的无界队列
SynchronousQueue不存储元素的阻塞队列,每一个put操作必须要等待一个take操作,否则不能继续添加元素
LinkedTransferQueue由链表实现的无界阻塞的TransferQueue,相对于其他的阻塞队列,多了tryTransfer()和transfer()方法。
LinkedBlockingDeque由链表实现的双向阻塞队列,双向阻塞队列的好处是在多线程入队时,减少竞争。

阻塞队列中提供的方法

操作抛出异常返回特殊值一直阻塞超时退出
插入add(e)offer(e)put(e)offer(e, time,unit)
移除remove()polltake()poll(time,util)
检查element()peek()不支持不支持
  • 抛出异常:如果队列满了,add- IllegalStateException(“Queue full”)。当队列空了,remove-NoSuchElementException。
  • 返回特殊值:offer-true/false 。poll-队列空返回null,不为空返回e。
  • 一直阻塞:队列满了阻塞put,队列空了阻塞take。
  • 超时退出:在阻塞队列加上阻塞的时间。

详解J.U.C中阻塞队列的使用

基于数组结构的阻塞队列ArrayBlockingQueue

public ArrayBlockingQueue(int capacity) {
    this(capacity, false);
}
public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}
public ArrayBlockingQueue(int capacity, boolean fair,
                          Collection<? extends E> c) {
    this(capacity, fair);

    final ReentrantLock lock = this.lock;
    lock.lock(); // Lock only for visibility, not mutual exclusion
    try {
        int i = 0;
        try {
            for (E e : c) {
                checkNotNull(e);
                items[i++] = e;
            }
        } catch (ArrayIndexOutOfBoundsException ex) {
            throw new IllegalArgumentException();
        }
        count = i;
        putIndex = (i == capacity) ? 0 : i;
    } finally {
        lock.unlock();
    }
}

基于链表的阻塞队列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();
    }
}

基于优先级阻塞队列PriorityBlockingQueue

public PriorityBlockingQueue() {
    this(DEFAULT_INITIAL_CAPACITY, null);
}
public PriorityBlockingQueue(int initialCapacity) {
    this(initialCapacity, null);
}
public PriorityBlockingQueue(int initialCapacity,
                             Comparator<? super E> comparator) {
    if (initialCapacity < 1)
        throw new IllegalArgumentException();
    this.lock = new ReentrantLock();
    this.notEmpty = lock.newCondition();
    this.comparator = comparator;
    this.queue = new Object[initialCapacity];
}
public PriorityBlockingQueue(Collection<? extends E> c) {
    this.lock = new ReentrantLock();
    this.notEmpty = lock.newCondition();
    boolean heapify = true; // true if not known to be in heap order
    boolean screen = true;  // true if must screen for nulls
    if (c instanceof SortedSet<?>) {
        SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
        this.comparator = (Comparator<? super E>) ss.comparator();
        heapify = false;
    }
    else if (c instanceof PriorityBlockingQueue<?>) {
        PriorityBlockingQueue<? extends E> pq =
            (PriorityBlockingQueue<? extends E>) c;
        this.comparator = (Comparator<? super E>) pq.comparator();
        screen = false;
        if (pq.getClass() == PriorityBlockingQueue.class) // exact match
            heapify = false;
    }
    Object[] a = c.toArray();
    int n = a.length;
    if (c.getClass() != java.util.ArrayList.class)
        a = Arrays.copyOf(a, n, Object[].class);
    if (screen && (n == 1 || this.comparator != null)) {
        for (int i = 0; i < n; ++i)
            if (a[i] == null)
                throw new NullPointerException();
    }
    this.queue = a;
    this.size = n;
    if (heapify)
        heapify();
}

延迟阻塞队列DelayQueue

public DelayQueue() {}
public DelayQueue(Collection<? extends E> c) {
    this.addAll(c);
}

无存储结构的阻塞队列SynchronousQueue

public SynchronousQueue() {
    this(false);
}
public SynchronousQueue(boolean fair) {
    transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}

阻塞队列的结合体LinkedTransferQueue

public LinkedTransferQueue() {
}
public LinkedTransferQueue(Collection<? extends E> c) {
    this();
    addAll(c);
}

双向阻塞队列LinkedBlockingDeque

public LinkedBlockingDeque() {
    this(Integer.MAX_VALUE);
}
public LinkedBlockingDeque(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
}
public LinkedBlockingDeque(Collection<? extends E> c) {
    this(Integer.MAX_VALUE);
    final ReentrantLock lock = this.lock;
    lock.lock(); // Never contended, but necessary for visibility
    try {
        for (E e : c) {
            if (e == null)
                throw new NullPointerException();
            if (!linkLast(new Node<E>(e)))
                throw new IllegalStateException("Deque full");
        }
    } finally {
        lock.unlock();
    }
}

阻塞队列的实现原理

阻塞队列的特性:如果队列为空,消费者线程会阻塞,如果队列满了,生产者线程会阻塞。怎么实现?

  • 如何让线程在满足某个他特定条件的情况下实现阻塞和唤醒。
  • 阻塞队列中的数据应该用什么样的容器来存储。

put()方法的说明

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;
  	// 初始化一个Node节点 单向链表结构
    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();
    }
    if (c == 0)
        signalNotEmpty();
}

take()方法说明

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;
}
  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

DougLiang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值