(三)实际项目中数据结构—队列的应用

1.队列:

定义: 一种数据结构,有非阻塞对列和阻塞对列,有界对列,无界队列
特点:先进先出
两种典型操作: 队尾添加、队头删除
几种对列:
非阻塞队列:当对列满或空时进行插入或者读取删除操作,抛出异常或者返回false,不对当前线程阻塞,没有同步或者唤醒策略。
阻塞列: 当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。
有界 一般来说队列的长度都预先定义好大小,这样的队列就是有界对列。
无界 不具有预定义容量的队列

2.阻塞队列使用:

多线程协调,合作

线程1往阻塞队列中添加元素,而线程2从阻塞队列中移除元素。

3.java中阻塞队列种类:

LinkedBlockingQueue:由链表结构组成的有界阻塞队列,此队列按 FIFO(先进先出)排序元素。此队列的默认和最大长度为Integer.MAX_VALUE。
LinkedTransferQueue由链表结构组成的无界阻塞队列。
LinkedBlockingDeque由链表结构组成的双向阻塞队列。
ArrayBlockingQueue: 由数组结构组成的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。构造时需要指定容量,并可以选择是否需要公平性。如果公平参数被设置true,等待时间最长的线程会优先得到处理,会使你在性能上付出代价。
PriorityBlockingQueue:支持优先级排序的无界阻塞队列,而不是先进先出队列。元素按优先级顺序被移除,该队列也没有上限,put时是不会受阻的,对列为空,take时就会阻塞。进入该队列中的元素要具有比较能力。默认情况下元素采取自然顺序排列,也可以通过比较器comparator来指定元素的排序规则。元素按照升序排列。
DelayQueue:是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或等于零的值时,则出现期满,poll就以移除这个元素了。此队列不允许使用 null 元素。
DelayQueue运用在以下应用场景:
缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。
定时任务调度。使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,从比如TimerQueue就是使用DelayQueue实现的。
SynchronousQueue:不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合于传递性场景,比如在一个线程中使用的数据,传递给另外一个线程使用,SynchronousQueue的吞吐量高于LinkedBlockingQueue 和 ArrayBlockingQueue。

4.阻塞队列控制线程操作实例:

多线程操作共同的队列时不需要额外的同步,队列会自动平衡负载,即那边(生产与消费两边)处理快了就会被阻塞掉,从而减少两边的处理速度差距。 不需要再单独考虑同步和线程间通信的问题。
public class Test {
	private int queueSize = 10;
	private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(queueSize);
	public static void main(String[] args) {
		Test test = new Test();
		Producer producer = test.new Producer();
		Consumer consumer = test.new Consumer();
		producer.start();
		consumer.start();
	}
	class Consumer extends Thread{
		@Override
		public void run() {
			consume();
		}
		private void consume() {
			while(true){
				try {
					queue.take();
					System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	class Producer extends Thread{
		@Override
		public void run() {
			produce();
		}
		private void produce() {
			while(true){
				try {
					queue.put(1);
					System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}













在并发编程中,一般推荐使用阻塞队列,这样实现可以尽量地避免程序出现意外的错误。
阻塞队列使用最经典的场景就是socket客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。还有其他类似的场景,只要符合生产者-消费者模型的都可以使用阻塞队列。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值