它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。头是最先加入的,尾是最近加入的。插入元素是追加到尾上。提取一个元素是从头提取。当多个线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。该队列不允许null元素。此实现采用了有效的“无等待 (wait-free)”算法,该算法基于 Maged M. Michael 和 Michael L. Scott 合著的 Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms 中描述的算法。
注意和大多数集合不一样。得到元素个数所花的时间是不确定。由于该队列的异步特性,确定当前的元素数量需要遍历的元素。这个类和它的迭代器实现了Collection和Iterator接口的所有可选方法。
内存一致性效果:和其他并发集合一样。把一个元素放入到队列的线程的优先级高与对元素的访问和移除的线程。
有三个函数需要注意的:
peek()获取元素 不移除头结点
poll() 获取元素并且在队列中移除,如果队列为空返回null
toArray
返回以恰当顺序包含此队列所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。如果指定的数组能容纳队列,则将该队列返回此处。否则,将分配一个具有指定数组的运行时类型和此队列大小的新数组。
如果指定的数组能容纳队列,并有剩余的空间(即数组的元素比队列多),那么会将紧接队列尾部的元素设置为 null。
像 toArray() 方法一样,此方法充当基于数组的 API 与基于 collection 的 API 之间的桥梁。更进一步说,此方法允许对输出数组的运行时类型进行精确控制,在某些情况下,这可以用来节省分配开销。
假定 x 是只包含字符串的一个已知队列。以下代码用来将该队列转储到一个新分配的 String 数组.
size:
返回此队列中的元素数量。如果此队列包含的元素数大于 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE。
需要小心的是,与大多数 collection 不同,此方法不是 一个固定时间操作。由于这些队列的异步特性,确定当前的元素数需要进行一次花费 O(n) 时间的遍历。
注意1:
ConcurrentLinkedQueue的.size() 是要遍历一遍集合的,很慢的,所以尽量要避免用size,
如果判断队列是否为空最好用isEmpty()而不是用size来判断.
注意问题1:
使用了这个ConcurrentLinkedQueue 类之后是否意味着我们不需要自己进行任何同步或加锁操作了呢?
如果直接使用它提供的函数,比如:queue.add(obj); 或者 queue.poll(obj);,这样我们自己不需要做任何同步。
但如果是非原子操作,比如:
- if(!queue.isEmpty()) {
- queue.poll(obj);
- }
所以对于这种情况,我们还是需要自己同步: 详见 (http://stackoverflow.com/questions/435069/java-util-concurrentlinkedqueue/435941 )
- synchronized(queue) {
- if(!queue.isEmpty()) {
- queue.poll(obj);
- }
- }
- public class conn{
- public static void main(String[] args) throws Exception{
- Queue<String> queue=new ConcurrentLinkedQueue<String>();
- for(int i=0;i<1000000;i++){
- queue.add(String.valueOf(i));
- }
- int num=10;//线程人个数
- for(int i=0;i<num;i++){
- new ThreadConn(queue);
- }
- }
- }
- public class ThreadConn implements Runnable{
- Queue<String> queue;
- public ThreadConn(Queue<String> queue){
- this.queue=queue;
- Thread thread=new Thread(this);
- thread.start();
- }
- public void run(){
- try{
- long sd=new Date().getTime();
- while(queue.poll()!=null){
- //这里是业务逻辑
- }
- System.out.println (sn-sd);
- }catch(Exception e){
- e.printStackTrace();
- }
- }
- }
启动10个线程
31
0
0
0
0
500
390
297
0
0
启动1个线程
360
10还没有1个快,如果poll这后的业务逻辑运行时间小的话,多线程序没有任何意义,
反之如果poll这后的业务逻辑运行时间相当于Thread.sleep(1);多线程确实起作用!
ConcurrentLinkedQueue 是基于链接节点的、线程安全的队列。并发访问不需要同步。因为它在队列的尾部添加元素并从头部删除它们,所以只要不需要知道队列的大小, ConcurrentLinkedQueue 对公共集合的共享访问就可以工作得很好。收集关于队列大小的信息会很慢,需要遍历队列。
- /*
- * 一个基于链接节点的、无界的、线程安全的队列。此队列按照 FIFO(先进先出)原则对元素进行排序。队列的头部 是队列中时间最长的元素。队列的尾部
- * 是队列中时间最短的元素。新的元素插入到队列的尾部,队列检索操作从队列头部获得元素。当许多线程共享访问一个公共 collection
- * 时,ConcurrentLinkedQueue 是一个恰当的选择。此队列不允许 null 元素。
- */
- private void concurrentLinkedQueueTest() {
- ConcurrentLinkedQueue<String> concurrentLinkedQueue = new ConcurrentLinkedQueue<String>();
- concurrentLinkedQueue.add("a");
- concurrentLinkedQueue.add("b");
- concurrentLinkedQueue.add("c");
- concurrentLinkedQueue.offer("d"); // 将指定元素插入到此队列的尾部。
- concurrentLinkedQueue.peek(); // 检索并移除此队列的头,如果此队列为空,则返回 null。
- concurrentLinkedQueue.poll(); // 检索并移除此队列的头,如果此队列为空,则返回 null。
- for (String str : concurrentLinkedQueue) {
- System.out.println(str);
- }
- }