BlockingQueue简介
BlockingQueue是Java中的一个线程安全的队列,实现了在进程间或线程间进行数据传递时的同步,而且比较高效。它支持多个线程同时放入或获取元素,并且在没有可用元素或者无法放入元素时会阻塞。
BlockingQueue特点
- 有界/无界:BlockingQueue可以选择有界或无界,有界的话需要指定容量大小。
- 先进先出/后进先出:根据你使用的BlockingQueue不同而定,常用的ArrayBlockingQueue是先进先出,而LinkedBlockingQueue是后进先出。
- 阻塞/非阻塞:BlockingQueue可以选择在队列已满或空时是否阻塞线程,也可以设置超时时间来自动返回。
BlockingQueue原理
BlockingQueue内部维护了一个数据结构,当队列已满时put()方法会阻塞当前线程,直到有空余空间;当队列为空时take()方法会阻塞当前线程,直到有新的元素被加入到队列中。
BlockingQueue提供了以下四个核心方法来实现队列的操作:
- put(E e):添加元素e到队列中,如果队列已满则会阻塞当前线程直到空间可用。
- take():移除并返回队列头部的元素,如果队列为空则会阻塞当前线程直到有新的元素被加入到队列中。
- offer(E e, long timeout, TimeUnit unit):添加元素e到队列中,如果队列已满则会阻塞当前线程直到超时或空间可用。
- poll(long timeout, TimeUnit unit):移除并返回队列头部的元素,如果队列为空则会阻塞当前线程直到超时或有新的元素被加入到队列中。
BlockingQueue通常用于线程间的通信或各种同步场景下的数据交换。例如,在一个生产者-消费者模型中,生产者线程不断往BlockingQueue中put元素,同时消费者线程也不断从BlockingQueue中take元素,这样就可以解耦生产者线程和消费者线程,从而极大地提高了系统的可扩展性和可维护性。
BlockingQueue源码讲解
BlockingQueue主要有以下实现类:
- ArrayBlockingQueue:基于数组的有界阻塞队列,队列以FIFO(先进先出)的方式插入元素。
- LinkedBlockingQueue:基于链表的无界阻塞队列,采用非公平锁,支持按照容量大小阻塞和按照时间阻塞。
- SynchronousQueue:简单而强大的无缓冲的同步队列,它只有put和take两种操作,并没有任何大小限制,其中一个线程在调用put方法之后会阻塞,直到另一个线程调用take方法为止。它没有内部缓存,它的一个线程的插入操作在等待另一个线程执行删除操作,并且都是通过Lock-Free(无锁)算法实现的,非常高效。
- PriorityBlockingQueue:基于堆的可阻塞优先级队列,支持优先级元素的排序,它内部采用了一个基于数组的完全二叉树结构,并对每一个元素按照它的元素优先级进行排序,优先级最高的元素将被放置在第一个元素位置。
每个BlockingQueue都可以被看作是一个先进先出的队列,其中每一个插入操作必须等待另一个线程的删除操作,反之亦然。 通常情况下,BlockingQueue会采用ReentrantLock或者synchronized关键字来实现内部的读写锁,以便于多线程进行访问。例如,在LinkedBlockingQueue中,插入元素时,如果队列已满,则当前线程会进入等待状态(通过notFull.await()进行等待),等待其他消费者来取走一些元素使得队列腾出空间。在另一方面,如果队列为空,则消费者线程会进入等待状态,等待前面的生产者线程往队列里添加元素。等待操作的实现都是通过内部的Condition进行的。
BlockingQueue的实现代码非常简单,特别是对于LinkedBlockingQueue来说。如果你想更深入地了解BlockingQueue的源代码,只需打开Java源代码并跟踪每个方法的调用即可。