1、队列
队列是一种特殊的线性结构,允许在线性结构的前端进行删除/读取操作;允许在线性结构的后端进行插入操作;这种线性结构具有“先进先出”的操作特点。
2、栈
栈也是一种线性结构,但是栈和队列相比只允许在线性结构的一端进行操作,入栈和出栈都是在一端完成。
3、有界队列
(1)SynchronousQueue(不存储元素的阻塞队列):
内部任何元素的阻塞队列,任何一次插入操作的元素都要等待相对的删除/读取操作,否则进行插入操作的线程就要一直等待,反之亦然。
SynchronousQueue<Object> queue = new SynchronousQueue<Object>();
// 不要使用add,因为这个队列内部没有任何容量,所以会抛出异常“IllegalStateException”
// queue.add(new Object());
// 操作线程会在这里被阻塞,直到有其他操作线程取走这个对象
queue.put(new Object());
(2)ArrayBlockingQueue(基于数组结构的有界的阻塞队列):
ArrayBlockingQueue 基于数组结构的有界的阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。
新元素插入队列的尾部,从头部获得元素。
这是一个典型的“有界缓存区”,一旦创建了这样的缓存区,就不能再增加其容量。
试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
// 我们创建了一个ArrayBlockingQueue,并且设置队列空间为2
ArrayBlockingQueue<Object> arrayQueue = new ArrayBlockingQueue<Object>(2);
// 插入第一个对象
arrayQueue.put(new Object());
// 插入第二个对象
arrayQueue.put(new Object());
// 插入第三个对象时,这个操作线程就会被阻塞。
arrayQueue.put(new Object());
// 请不要使用add操作,和SynchronousQueue的add操作一样,它们都使用了AbstractQueue中的add实现
4、无界队列
(1)LinkedBlockingQueue(基于单向链表的无界的阻塞队列):
LinkedBlockingQueue 是单链表的阻塞队列,尾部 插入元素,头部 取出元素;
其特点是 FIFO(先进先出)。
LinkedBlockingQueue 是我们在 ThreadPoolExecutor 线程池中常用的等待队列。它可以指定容量也可以不指定容量。由于它具有“无限容量”的特性,所以我还是将它归入了无限队列的范畴(实际上任何无限容量的队列/栈都是有容量的,这个容量就是Integer.MAX_VALUE)。
LinkedBlockingQueue 的实现是基于链表结构,如果指定了的容量大小,那么它反映出来的使用特性就和 ArrayBlockingQueue 类似了。
(2)LinkedBlockingDeque(基于双向链表的无界的阻塞队列):
LinkedBlockingDeque 是一个由链表结构组成的双向阻塞队列,即可以从队列的两端(头部和尾部)插入和移除元素。 双向队列因为多了一个操作队列的入口,在多线程同时入队时,也就减少了一半的竞争。 所以, LinkedBlockingDeque 既可以从尾部插入/取出元素,还可以从头部插入元素/取出元素。
(3)PriorityBlockingQueue
PriorityBlockingQueue 是一个按照优先级进行内部元素排序的无限队列。存放在PriorityBlockingQueue 中的元素必须实现 Comparable 接口,这样才能通过实现compareTo()方法进行排序。优先级最高的元素将始终排在队列的头部; PriorityBlockingQueue 不会保证优先级一样的元素的排序,也不保证当前队列中除了优先级最高的元素以外的元素,随时处于正确排序的位置。
(4)LinkedTransferQueue LinkedTransferQueue 也是一个无限队列,它除了具有一般队列的操作特性外(先进先出),还具有一个阻塞特性:LinkedTransferQueue可以由一对生产者/消费者线程进行操作,当消费者将一个新的元素插入队列后,消费者线程将会一直等待,直到某一个消费者线程将这个元素取走,反之亦然。
5、线程的信号量
Semaphore是一种基于计数的信号量。它可以设定一个阀值,基于此,多个线程竞争获取许可信号,做自己的申请后归还,超过阀值后,线程申请许可信号将会被阻塞。
Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。 计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。计数信号量还可以用来实现某种资源池,或者对容器施加边界。
Semaphore中管理者一组虚拟的许可,许可的数量可通过构造函数来指定。在互相操作时可以首先获得许可(只要还有剩余的许可),并在使用以后释放许可。如果没有许可,那么acquire将阻塞直到有许可(或者直到被中断或者操作超时)。release方法将返回一个许可给信号量。计算信号量的一种简化形式是二值信号量,即初始值为1的Semaphore,二值信号量可以用做互斥体,并不具备可重入的加锁语义:谁拥有这个唯一的许可,谁就拥有了互斥锁。
Semaphore可以用来实现资源池,例如数据库连接池。我们可以构造一个固定长度的资源池,当池为空时,请求资源将会失败,但你真正希望看到的行为是阻塞而不是失败,并且当池非空时解除阻塞。如果将Semaphore的计数值初始化为池的大小,并在从池中获取一个资源之前先首先调用acquire方法获取一个许可,在将资源返回给池之后调用release释放许可,那么acquire将一直阻塞直到资源池不为空。