• java基础解析系列(一)---String、StringBuffer、StringBuilder
  • java基础解析系列(二)---Integer缓存及装箱拆箱
  • java基础解析系列(三)---HashMap原理
  • java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现
  • java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别
  • java基础解析系列(六)---注解原理及使用
  • java基础解析系列(七)---ThreadLocal原理分析
  • 这是我的博客目录,欢迎阅读

先看一个例子

class Te1 extends Thread
{private List<Integer> list;public Te1(List<Integer> list){this.list = list;}public void run(){Iterator<Integer> iterator = list.iterator();while(iterator.hasNext()){int i = iterator.next();}}
}class Te2 extends Thread
{private List<Integer> list;public Te2(List<Integer> list){this.list = list;}public void run(){for (int i = 0; i < list.size(); i++){list.remove(i);}}
}
public class Test {public static void main(String[] args) {ArrayList<Integer> list=new ArrayList();for (int i = 0; i <100 ; i++) {list.add(i);}Te1 t1=new Te1(list);Te2 t2=new Te2(list);t1.start();t2.start();}
}
  • 一个线程迭代,一个线程进行删除,运行时抛出ConcurrentModificationException异常

ConcurrentModificationException

  • 中文意思为并发修改异常

    736     public Iterator<E> iterator() {
    737         return new Itr();
    738     }
    743     private class Itr implements Iterator<E> {
    744         int cursor;       // index of next element to return
    745         int lastRet = -1; // index of last element returned; -1 if no such
    746         int expectedModCount = modCount;
    747
    748         public boolean hasNext() {
    749             return cursor != size;
    750         }
    751
    752         @SuppressWarnings("unchecked")
    753         public E next() {
    754             checkForComodification();...
    763         }
    764
    765         public void remove() {
    766             if (lastRet < 0)
    767                 throw new IllegalStateException();
    768             checkForComodification();...
    778         }
    779
    780         final void checkForComodification() {
    781             if (modCount != expectedModCount)
    782                 throw new ConcurrentModificationException();
    783         }
    784     }
  • ArrayList有一个内部类Itr,从源码可以看到这个类的next和remove方法里面都调用了一个chechForModification方法,而从这个方法(780行)的源码可以看到,他是通过判断modCount和expectedModCount是否相等来决定是否抛出并发修改异常
  • 同时在这个内部类可以看expectedModCount初始化为modCount(746行),后面并没有修改

    377     public boolean add(E e) {
    378         ensureCapacity(size + 1);  // Increments modCount!!...
    381     }
    178     public void ensureCapacity(int minCapacity) {
    179         modCount++;
    180         ...
    189     }
    439     public boolean remove(Object o) {
    440         if (o == null) {
    441             for (int index = 0; index < size; index++)
    442                 if (elementData[index] == null) {
    443                     fastRemove(index);
    444                     return true;
    445                 }
    446         } else {
    447             for (int index = 0; index < size; index++)
    448                 if (o.equals(elementData[index])) {
    449                     fastRemove(index);
    450                     return true;
    451                 }
    452         }
    453         return false;
    454     }
    460     private void fastRemove(int index) {
    461         modCount++;...
    467     }
  • 从ArrayList的add和remove方法源码可以看到,这两个方法都会导致modCount的改变
  • 那么可以分析为什么之前的代码会抛出异常,线程A进行迭代,此时expectedModCount已经确定了,后面并没有进行修改,而此时线程B同时remove,从前面知道remove会导致modCount改变,此时两者不同导致抛出异常

fail-fast

A fail-fast system is nothing but immediately report any failure that is likely to lead to failure. When a problem occurs, a fail-fast system fails immediately.
In Java, we can find this behavior with iterators. In case, you have called iterator on a collection object, and another thread tries to modify the collection object, then concurrent modification exception will be thrown. This is called fail-fast.
  • 中文译为快速失败,这是一种错误检测机制。
  • 对上文进行翻译,当在对一个集合进行迭代的时候,其他线程尝试去修改这个集合,并发修改异常会被抛出。这就叫做快速失败。

CopyOnWriteArrayList

  • CopyOnWriteArrayList可以解决fail-fast的问题,将ArrayList替换成CopyWriteArrayList进行试验。

    public class Test {
    public static void main(String[] args) {CopyOnWriteArrayList<Integer> list=new CopyOnWriteArrayList();for (int i = 0; i <100 ; i++) {list.add(i);}Te1 t1=new Te1(list);Te2 t2=new Te2(list);t1.start();t2.start();}
  • 结果发现并没有抛出异常,下面从源码角度来分析
  • CopyOnWriteArrayList的remove方法

469     public E remove(int index) {470         final ReentrantLock lock = this.lock;
471         lock.lock();
472         try {
473             Object[] elements = getArray();
474             int len = elements.length;
475             E oldValue = get(elements, index);
476             int numMoved = len - index - 1;
477             if (numMoved == 0)
478                 setArray(Arrays.copyOf(elements, len - 1));
479             else {
480                 Object[] newElements = new Object[len - 1];
481                 System.arraycopy(elements, 0, newElements, 0, index);
482                 System.arraycopy(elements, index + 1, newElements, index,
483                                  numMoved);
484                 setArray(newElements);
485             }
486             return oldValue;
487         } finally {
488             lock.unlock();
489         }
490     }
99      final void setArray(Object[] a) {
100         array = a;
101     }
  • 473行获取当前的Object数组,480行创建一个新的Object数组,再将旧的数组复制到新的数组上,484行将array指向新的数组

    956     public Iterator<E> iterator() {
    957         return new COWIterator<E>(getArray(), 0);
    958     }
    991     private static class COWIterator<E> implements ListIterator<E> {
    992
    993         private final Object[] snapshot;
    994
    995         private int cursor;
    996
    997         private COWIterator(Object[] elements, int initialCursor) {
    998             cursor = initialCursor;
    999             snapshot = elements;
    1000        }
    1001
    1002        public boolean hasNext() {
    1003            return cursor < snapshot.length;
    1004        }
    1005
    1010        @SuppressWarnings("unchecked")
    1011        public E next() {
    1012            if (! hasNext())
    1013                throw new NoSuchElementException();
    1014            return (E) snapshot[cursor++];
    1015        }
    1016
  • 999行将snapshot指向当前的array
  • 1011行执行next方法返回snapshot中元素,那么在遍历的过程,如果其他线程执行remove并将array指向了新创建的数组,这个snapshot并没有更新为新的数组,仍然指向的是remove之前的数组
  • 从CopyOnWriteArrayList的迭代器也可以发现没有fail-fast机制.

CopyOnWriteArrayList分析

  • 修改代价大,可以从源码知道,remove还是add方法,都会进行一次数组的复制,这样消耗了空间(可能导致gc的频率提高)也消耗了时间
  • 读写分离,读写不一致,读的时候读的是旧的数组,写的时候写的是新的数组,所以读的时候不一定是最新的
  • 读的时候不需要进行加锁,因为写的时候是写在新的数组,读的数组是旧的数组,并不会改变
  • 因此,CopyOnWriteArrayList适合读多写少的场景

我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)

作者:jiajun 出处: http://www.cnblogs.com/-new/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。

标签:  java基础解析
好文要顶  关注我  收藏该文   

jiajun_geek
关注 - 0
粉丝 - 165

+加关注

关注我~
1

« 上一篇: java基础解析系列(七)---ThreadLocal原理分析

posted @  2017-10-09 08:55  jiajun_geek 阅读( 227) 评论( 2)  编辑  收藏

最怕一生碌碌无为,还说平凡难能可贵。相关推荐

  1. 如果你一生碌碌无为是因为努力不够?

    什么是碌碌无为,每个人都有不同的看法,有人认为赚到钱了就叫成功,没赚到钱的就叫碌碌无为,那么我想请问: 岳飞既没赚到钱,也没保住性命,也没有北定中原,他是成功还是失败: 文天祥国家都灭亡了,他是成功还 ...

  2. 怕研究生碌碌无为?那应该好好看看这篇文章

    ​大家好,我是羽峰,今天要和大家分享的是一些读研期间的建议,目前研究生负面新闻还是比较多的,所以写下这篇,希望给那些新入学,或者没有入学的研究生一些建议. 还是老话,我是羽峰,希望我所分享的文章能为您 ...

  3. 自动按键脚本_阔怕...按键精灵还能将别人的脚本导入为自己所用

    在按键精灵中你可以自己编辑脚本也可以直接导入别人的脚本直接使用.但是按键精灵的脚本文件是按键一定的数据格式保存的,如果单纯地把Q文件里面的内容直接复制到编辑器上是无法识别的.那如何导入别人的脚本? 一 ...

  4. 记住这10个学习资源网站,让你受益一生,还可以省学费

    又到一年毕业季,离开了校园,并不代表着你就能放下书本.反而在工作中你才知道自己缺乏的知识和技能,真正的学习才刚刚开始了. 没有了校园和老师,以后就能靠你自己努力了.小编今天给大家推荐32个资源网站,里 ...

  5. 为人示弱,做事留余 | 摸鱼系列

    我很喜欢结交有很好的自然观察能力的朋友,这是种对周围环境和文化的洞察力. 一方面的原因是优秀的领导者.企业家和投资人能利用这种能力发现新市场,预测新潮流,设计出有效的市场营销活动,并找到需要重点关注的 ...

  6. pip is configured with locations that require TLS/SSL 解决

    pip安装numpy时报错: pip is configured with locations that require TLS/SSL 解决方法 其实还是 Anaconda 环境变量的问题. 进入  ...

  7. 致敬金庸:武侠版编程语言...Java像张无忌还是令狐冲?

    我就喜欢这样,等新闻消失,热点过去,人们快要遗忘的时候, 用自己的方式,想起他.让他被人想起. 短评:夫千里之远,不足以举其大:千仞之高,不足以举其深.<倚天屠龙记> 短评:这世间和张三丰 ...

  8. 全栈溯源、mAPM、金融性能、Oracle VS. MySQL:看APM技术专场有哪些干货

    在日益复杂的应用环境中,网络.移动端.浏览器端.服务端的性能问题种类繁多,如何精准的定位问题根源,并留住用户是关键问题.尤其是云计算平台的普及使用,更是对应用性能的追踪和优化提出了新的拷问.在此前提下 ...

  9. 教程:这个难到几乎无人通关的游戏,在它面前就是渣!

    Flappy Bird这个游戏相信大家都知道,曾经因为难度虐人而爆红,也因为难度变态而被舆论谩骂.被迫下架.但是当它遇上机器学习,就是个渣. 今天给大家介绍的这个教程,就是使用机器学习来玩转Flapp ...

最新文章

  1. Linux fstab参数详解
  2. java语言简单代码_java语言编程如何实现一个最简单程序?
  3. python实现排序算法_python实现·十大排序算法之插入排序(Insertion Sort)
  4. 虚拟主机的实现方式,真是简单啊!
  5. Android中的音频播放(MediaPlayer和SoundPool)
  6. 基于TCP的在线聊天程序
  7. var arr = [] 与 var arr = new Array();
  8. 【ZOJ - 3211】Dream City (01背包类问题,贪心背包)
  9. oracle存储sql片段引入_强大的跨数据库访问组件 UniDAC使用教程:注释和SQL函数...
  10. 【嵌入式Linux】嵌入式Linux应用开发基础知识之文件IO
  11. 网易MCtalk泛娱乐科技峰会:泛娱乐的未来时代属于科技创新者
  12. 圆形led屏幕_展示厅LED大屏幕安装价格/芮城
  13. 条款6:明确拒绝编译器自动生成的函数
  14. 亚马逊股价继续大涨 首度突破每股800美元
  15. Visio 2019 专业版 下载地址
  16. 电脑自动安装软件、各种弹窗广告、中病毒等问题解决方案
  17. 教你如何用PS轻松制作ico图标
  18. 力扣:第 304 场周赛
  19. 虚拟化技术的演变过程和KVM虚拟化的简介
  20. ARM各版本架构区别,各架构的系列芯片。

热门文章

  1. 倍福--AX5000驱动器的参数设置
  2. (c语言)三角形判断
  3. 中职计算机英语教学计划,职高的英语教学计划
  4. Mac电脑想要快速返回桌面,简单几步即可搞定!
  5. PHP语言的四大优势和八大特性
  6. JAVA保姆级教学(一)[安装及使用]
  7. iOS 图片压缩方法
  8. java 邮件 定时_java实现邮件定时发送
  9. 用Python图像识别技术打造一个小狗分类器,实现让机器自己去“学习”~
  10. 数组对象和类数组对象区别