在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列(先进先出)。Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列,都不能存放null值,如果是并发异步处理,如日志等,得选用非阻塞队列concurrentLinkedQueue。
1、BlockingQueue下有两个常用子类LinkedBlockingQueue和ArrayBlockingQueue,内部分链表和数组实现,要想实现获取元素和存放元素的线程安全阻塞,那么只能用里面线程安全的两个方法put()和take()来实现,阻塞的意思就是此条线程会处于等待卡死状态,解锁的条件是队列中有另一条线程存入或取出数据了,就会解锁,说白了就是满了不能再存,空了不能取,都得等着,就相当于队列是仓库,仓库没有货了就生产,仓库有货就能消费,锁条件是notFull和notEmpty,此种队列多用于生产者消费者模式,new之前最好指定容量大小,不然默认是Integer.MAX_VALUE的大小。
2、ConcurrentLinkedQueue是非阻塞队列,其没有put和take方法,可以无限制存,且是线程安全的,N个用户同时存也能保证每次存放在队尾而不乱掉,但是其的size()方法会不定时遍历,所以特耗时,开发时不要用,如果判断是否有元素可以使用isEmpty()方法,千万不要用size()>0来判断是否有元素ConcurrentLinkedQueue的queue.add(obj); 或者 queue.poll(obj);是线程安全的,但是其他方法并没有保证线程安全,如判断isEmpty()所以在多线程时还得自己加锁
synchronized(queue) { if(!queue.isEmpty()) { queue.poll(obj); } }
3、队列的几个取元素方法区别:
①、add方法在添加元素的时候,若超出了度列的长度会直接抛出异常:
②、对于put方法,若向队尾添加元素的时候发现队列已经满了会发生阻塞一直等待空间,以加入元素。
③、offer方法在添加元素时,如果发现队列已满无法添加的话,会直接返回false。
4、从队列中取出并移除头元素的方法有:poll,remove,take。
poll: 若队列为空,返回null。
remove:若队列为空,抛出NoSuchElementException异常。
take:若队列为空,发生阻塞,等待有元素。
这里简单对其重复的方法做点简单的区分。
offer,add区别:
一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,多出的项就会被拒绝。无大小限制的ConcurrentLinkedQueue队列,这两个方法没有区别。
这时新的 offer 方法就可以起作用了。它不是对调用 add() 方法抛出一个 unchecked 异常,而只是得到由 offer() 返回的 false。
poll,remove区别:
remove() 和 poll() 方法都是从队列中删除第一个元素。remove() 的行为与 Collection 接口的版本相似,
但是新的 poll() 方法在用空集合调用时不是抛出异常,只是返回 null。因此新的方法更适合容易出现异常条件的情况。
peek,element区别:
element() 和 peek() 用于在队列的头部查询元素。与 remove() 方法类似,在队列为空时, element() 抛出一个异常,而 peek() 返回 null,peek()和element()方法仅仅查询头部元素,不会移除