写这篇文章源于我经历过的一次生产事故,在某家公司的时候,有个服务会收集业务系统的日志,此服务的开发人员在给业务系统的sdk中就因为使用了LinkedList,又没有做并发控制,就造成了此服务经常不能正常收集到业务系统的日志(丢日志以及日志上报的线程停止运行)。看一下add()方法的源码,我们就可以知道原因了:

  1. public boolean add(E e) {
  2. linkLast(e);//调用linkLast,在队列尾部添加元素
  3. return true;
  4. }
  5.  
  6. void linkLast(E e) {
  7. final Node<E> l = last;
  8. final Node<E> newNode = new Node<>(l, e, null);
  9. last = newNode;
  10. if (l == null)
  11. first = newNode;
  12. else
  13. l.next = newNode;
  14. size++;//多线程情况下,如果业务系统没做并发控制,size的数量会远远大于实际元素的数量
  15. modCount++;
  16. }

demo  Lesson2LinkedListThreads 展示了在多线程且没有做并发控制的环境下,size的值远远大于了队列的实际值,100个线程,每个添加1000个元素,最后实际只加进去2030个元素:

List的变量size值为:88371
         第2031个元素取出为null

解决方案,使用锁或者使用ConcurrentLinkedQueue、LinkedBlockingQueue等支持添加元素为原子操作的队列。

上一节我们已经分析过LinkedBlockingQueue的put等方法的源码,是使用ReentrantLock来实现的添加元素原子操作。我们再简单看一下高并发queue的add和offer()方法,方法中使用了CAS来实现的无锁的原子操作:

   public boolean add(E e) {
       return offer(e);
     }

  1. public boolean offer(E e) {
  2. checkNotNull(e);
  3. final Node<E> newNode = new Node<E>(e);
  4.  
  5. for (Node<E> t = tail, p = t;;) {
  6. Node<E> q = p.next;
  7. if (q == null) {
  8. // p is last node
  9. if (p.casNext(null, newNode)) {
  10. // Successful CAS is the linearization point
  11. // for e to become an element of this queue,
  12. // and for newNode to become "live".
  13. if (p != t) // hop two nodes at a time
  14. casTail(t, newNode); // Failure is OK.
  15. return true;
  16. }
  17. // Lost CAS race to another thread; re-read next
  18. }
  19. else if (p == q)
  20. // We have fallen off list. If tail is unchanged, it
  21. // will also be off-list, in which case we need to
  22. // jump to head, from which all live nodes are always
  23. // reachable. Else the new tail is a better bet.
  24. p = (t != (t = tail)) ? t : head;
  25. else
  26. // Check for tail updates after two hops.
  27. p = (p != t && t != (t = tail)) ? t : q;
  28. }
  29. }

  接下来,我们再利用高并发queue对上面的demo进行改造,大家只要改变demo中的内容,讲下面两行的注释内容颠倒,即可发现没有丢失任何的元素:

public static LinkedList list = new LinkedList();
      //public static ConcurrentLinkedQueue list = new ConcurrentLinkedQueue();

再看一下高性能queue的poll()方法,才觉得NB,取元素的方法也用CAS实现了原子操作,因此在实际使用的过程中,当我们在不那么在意元素处理顺序的情况下,队列元素的消费者,完全可以是多个,不会丢任何数据:

  1. public E poll() {
  2. restartFromHead:
  3. for (;;) {
  4. for (Node<E> h = head, p = h, q;;) {
  5. E item = p.item;
  6.  
  7. if (item != null && p.casItem(item, null)) {
  8. // Successful CAS is the linearization point
  9. // for item to be removed from this queue.
  10. if (p != h) // hop two nodes at a time
  11. updateHead(h, ((q = p.next) != null) ? q : p);
  12. return item;
  13. }
  14. else if ((q = p.next) == null) {
  15. updateHead(h, p);
  16. return null;
  17. }
  18. else if (p == q)
  19. continue restartFromHead;
  20. else
  21. p = q;
  22. }
  23. }
  24. }

关于ConcurrentLinkedQueue和LinkedBlockingQueue:

1.LinkedBlockingQueue是使用锁机制,ConcurrentLinkedQueue是使用CAS算法,虽然LinkedBlockingQueue的底层获取锁也是使用的CAS算法

2.关于取元素,ConcurrentLinkedQueue不支持阻塞去取元素,LinkedBlockingQueue支持阻塞的take()方法,如若大家需要ConcurrentLinkedQueue的消费者产生阻塞效果,需要自行实现

3.关于插入元素的性能,从字面上和代码简单的分析来看ConcurrentLinkedQueue肯定是最快的,但是这个也要看具体的测试场景,我做了两个简单的demo做测试,测试的结果如下,两个的性能差不多,但在实际的使用过程中,尤其在多cpu的服务器上,有锁和无锁的差距便体现出来了,ConcurrentLinkedQueue会比LinkedBlockingQueue快很多:

demo Lesson2ConcurrentLinkedQueuePerform:在使用ConcurrentLinkedQueue的情况下100个线程循环增加的元素数为:33828193

demo Lesson2LinkedBlockingQueuePerform:在使用LinkedBlockingQueue的情况下100个线程循环增加的元素数为:33827382

Lesson2.1:LinkedList、ConcurrentLinkedQueue、LinkedBlockingQueue对比分析的更多相关文章

  1. ArrayList和LinkedList的几种循环遍历方式及性能对比分析(转)

    主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性能测试对比,根据ArrayList和LinkedList的源码实现分析性能结果,总结结论. 通过本文你可以 ...

  2. ArrayList和LinkedList的几种循环遍历方式及性能对比分析

    最新最准确内容建议直接访问原文:ArrayList和LinkedList的几种循环遍历方式及性能对比分析 主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性 ...

  3. ArrayList和LinkedList的几种循环遍历方式及性能对比分析(转载)

    原文地址: http://www.trinea.cn/android/arraylist-linkedlist-loop-performance/ 原文地址: http://www.trinea.cn ...

  4. ArrayList和LinkedList遍历方式及性能对比分析

    ArrayList和LinkedList的几种循环遍历方式及性能对比分析 主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性能测试对比,根据ArrayLis ...

  5. 【转】ArrayList和LinkedList的几种循环遍历方式及性能对比分析

    原文网址:http://www.trinea.cn/android/arraylist-linkedlist-loop-performance/ 主要介绍ArrayList和LinkedList这两种 ...

  6. Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 [ 转载 ]

    Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 @author Trinea 原文链接:http://www.trinea.cn/android/arrayl ...

  7. (转)ArrayList和LinkedList的几种循环遍历方式及性能对比分析

    主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性能测试对比,根据ArrayList和LinkedList的源码实现分析性能结果,总结结论. 通过本文你可以 ...

  8. List集合总结,对比分析ArrayList,Vector,LinkedList

    前面已经写了三篇关于Java集合的文章,包括: Java集合 ArrayList原理及使用 再说Java集合,subList之于ArrayList Java集合 LinkedList的原理及使用 关于 ...

  9. c#数据结构之Array、ArrayList、List、LinkedList对比分析

    一.前言: 在c#数据结构中,集合的应用非常广泛,无论是做BS架构还是CS架构开发,都离不开集合的使用,比如我们常见的集合包括:Array.ArrayList.List.LinkedList等.这一些 ...

随机推荐

  1. 像table一样布局div的CSS属性详解

    .equal {                     display:table;                     border-collapse:separate;margin: aut ...

  2. CentOs上搭建git服务器

    CentOs上搭建git服务器 首先安装setuptools wget http://pypi.python.org/packages/source/s/setuptools/setuptools-0 ...

  3. Xcode 运行报错:“Your build settings specify a provisioning profile with the UUID ****** however, no such provisioning profile was found”

    iOS开发中遇到"Your build settings specify a provisioning profile with the UUID ****** however, no su ...

  4. [转]《深度探索C++对象模型》读书笔记[一]

    前 言 Stanley B.Lippman1.   任何对象模型都需要的三种转换风味: ü        与编译器息息相关的转换 ü        语言语义转换 ü        程序代码和对象模型的 ...

  5. Python----Tornado安装

    Tornado安装,环境准备:          1.python安装包及安装 2.Tornado安装包 Python包安装 Linux下安装 如果使用的是 Linux系统 或 Mac OS X ,系 ...

  6. 第几天 AC 杭电

    第几天? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  7. ios app 开发中ipa重新签名步骤介绍-备

    作为一个app应用程序开发者,在app应用程序在苹果商店上架前总需要将安装包安装到ios机器上进行测试,这个时候我们就需要打包in house版本的ipa了,打包in house实际上是一个将ipa应 ...

  8. Top 100 English Verbs

    accept allow ask believe borrow break bring buy can/be able cancel change clean comb complain cough ...

  9. DES加解密实现方式

    private static readonly byte[] _keys = { 0x22, 0x84, 0x56, 0x98, 0x90, 0xAB, 0xpD, 0xEF }; private s ...

  10. Visual Studio 2015 Owin+MVC+WebAPI+ODataV4+EntityFrawork+Identity+Oauth2.0+AngularJS 1.x 学习笔记

    2016年,.net 会有很多大更新 ASP.NET 5 在此之前我都是用着古老的.net做开发的 (WebForm + IIS) 为了接下来应对 .net 的新功能,我特地去学习了一下基本的 MVC ...