www.2527.com_澳门新葡8455手机版_新京葡娱乐场网址_
做最好的网站

AlibabaJava开采手册

2019-10-06 04:11 来源:未知

1.前言

近来在上下班的客车上把《阿里Baba(Alibaba)Java开垦手册》读了三次,感觉受益良多。读的经过中也是有一部分心想和迷离,就抽时间亲自品尝了弹指间。上面用那篇文章把本身的标题和答案整理出来。

在前边一篇小说中关系,对Vector、ArrayList在迭代的时候要是同反常候对其开展修改就能够抛出java.util.ConcurrentModificationException分外。上边大家就来谈谈以下那一个那些出现的案由以及解决办法。

目录大纲
<a href="#part_01"> 1. ConcurrentModificationException十一分出现的因由 </a>
<a href="#part_02"> 2. 在单线程碰着下的解决办法 </a>
<a href="#part_03"> 3. 在八线程碰到下的化解措施 </a>

2.正文

  1. 手册里提到,如若重写equals()方法,就无法不重写hashCode()方法,网络查了弹指间哪些重写hashCode(),开掘大多种写方法里都出现了31以此玄妙的数字。以下是String类的hashCode()达成:
@Override public int hashCode() { int hash = hashCode; if (hash == 0) { if (count == 0) { return 0; } for (int i = 0; i < count;   i) { hash = 31 * hash   charAt; } hashCode = hash; } return hash; }

这么些magic number勾起了自己的兴味,重写hashCode()方法为啥会异口同声地用到31那个数字呢?在网络查了一下,那么些题材也未尝正规的答案,以下是本人查找到的有个别比较相信的答案:

  • 每种对象根据值总计HashCode,这一个code大小纵然不奢求必得独一(因为如此日常总计会一点也不快),不过要尽量的绝不再一次,因此基数要尽也许的大且总括出来的值不要溢出,计算出来的hash地址越大,所谓的“争执”就越少,查找起来功用也会加强。
  • 素数的表征能够使得它和别的数相乘后获得的结果比别的艺术更便于产成独一性,也便是hashcode值的争论可能率一点都不大。
  • 31*N能够被编写翻译器优化为左移5位后减N,有较高的质量。

正如高尚的答案是Stack OverFlow上:威驰ing to Joshua Bloch's『Effective Java』:The value 31 was chosen because it is an odd prime. If it were even and the multiplication overflowed, information would be lost, as multiplication by 2 is equivalent to shifting.The advantage of using a prime is less clear, but it is traditional.A nice property of 31 is that the multiplication can be replaced by a shift and a subtraction for better performance: 31 * i == (i << 5) - i.Modern VMs do this sort of optimization automatically.

翻译:依照 Joshua Bloch 的行文『Effective Java』:设计者选用 31 这几个值是因为它是三个奇质数。假设它是八个偶数,在采纳乘法当中产生数值溢出时,原有数字的新闻将会遗弃,因为乘以二一定于位移。选用质数的优势不是那么清晰,但是那是三个价值观。31 的八个可观的属性是:乘法能够被位移和减法代替: 31 * i == (i << 5)

  • i当代的 VM 能够活动达成这些优化。
  1. 手册里还预留了叁个标题:
List<String> a = new ArrayList<String>(); a.add; a.add; for (String temp : a) { if ("1".equals { a.remove; } }

这段代码施行时不会抛出极其,可是若是将equals后面包车型大巴"1"换到"2"就能够抛出ConcurrentModificationException,使用Iterator来开展删减操作也不会抛出极度,那是为什么吧?

透过深入分析源码开掘,在Iterator的remove方法中都调用了checkForComodification()方法:

final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }

哪怕在这里抛出了ConcurrentModificationException.

modCount:修改次数expectedModCount:预期的修改次数

采用ArrayList的remove(),只会使modCount ,不会修改expectedModCount使用Iterator的remove(),则是expectedModCount = modCount

前方提到remove时不会抛出非常,那是因为foreach方法其实也是调用了Iterator的hasNext,next()里调了checkForComodification(),hasNext()却没调用,remove后,hasNext()重返false,就不会走到next(),所以也就不会抛出非常了,那其实只是二个戏剧性。所以:不用在foreach循环里进行成分的remove/add操作,remove成分请使用Iterator格局,如若出现操作,必要对Iterator对象加锁。现实的解析进程能够参照:

1. ConcurrentModificationException格外出现的源委

先看上边这段代码:

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                list.remove(integer);
        }
    }
}

运营结果:

图片 1

ConcurrentModificationException.png

  从十分音信能够发掘,格外出现在checkForComodification()方法中。

大家不忙看checkForComodification()方法的切实可行达成,大家先依照程序的代码一步一步看ArrayList源码的贯彻:

先是看ArrayList的iterator()方法的现实性达成,查看源码发掘在ArrayList的源码中并不曾iterator()那么些法子,那么很刚毅这几个法子应该是其父类只怕完毕的接口中的方法,大家在其父类AbstractList中找到了iterator()方法的现实贯彻,下边是其促成代码:

public Iterator<E> iterator() {
    return new Itr();
}

从这段代码能够看来再次回到的是二个针对性Itr类型对象的援用,大家跟着看Itr的实际达成,在AbstractList类中找到了Itr类的现实贯彻,它是AbstractList的二个分子内部类,下边这段代码是Itr类的具有达成:

private class Itr implements Iterator<E> {

    int cursor = 0;
    int lastRet = -1;
    int expectedModCount = modCount;

    public boolean hasNext() {
           return cursor != size();
    }
    public E next() {
        checkForComodification();
        try {
            E next = get(cursor);
            lastRet = cursor  ;
            return next;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
    }
    public void remove() {
        if (lastRet == -1)
            throw new IllegalStateException();
        checkForComodification();

        try {
            AbstractList.this.remove(lastRet);
            if (lastRet < cursor)
               cursor--;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException e) {
            throw new ConcurrentModificationException();
        }
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

率先大家看一下它的多少个分子变量:

cursor:表示下一个要拜见的要素的目录,从next()方法的切实落到实处就可见到
  lastRet:表示上四个拜候的成分的目录
  expectedModCount:表示对ArrayList修改次数的梦想值,它的伊始值为modCount。
  modCount是AbstractList类中的三个分子变量

protected transient int modCount = 0;

该值表示对List的修改次数,查看ArrayList的add()和remove()方法就能够发掘,每一回调用add()方法恐怕remove()方法就能对modCount进行加1操作。

好了,到这里我们再看看下面的顺序:

当调用list.iterator()再次回到贰个Iterator之后,通过Iterator的hashNext()方法决断是还是不是还大概有成分未被访谈,大家看一下hasNext()方法,hashNext()方法的落到实处很简短:

public boolean hasNext() {
    return cursor != size();
}

接下来经过Iterator的next()方法赢得到下标为0的成分,大家看一下next()方法的现实性实现:

public E next() {
   checkForComodification();
   try {
      E next = get(cursor);
      lastRet = cursor  ;
      return next;
   } catch (IndexOutOfBoundsException e) {
      checkForComodification();
      throw new NoSuchElementException();
   }
}

那边是非凡关键的地点:首先在next()方法中会调用checkForComodification()方法,然后依照cursor的值获取到成分,接着将cursor的值赋给lastRet,并对cursor的值实行加1操作。早先时,cursor为0,lastRet为-1,那么调用三次之后,cursor的值为1,lastRet的值为0。注意此时,modCount为0,expectedModCount也为0。

随后往下看,程序中决断当前因素的值是不是为2,若为2,则调用list.remove()方法来删除该因素。

小编们看一下在ArrayList中的remove()方法做了怎么着:

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index  )
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index  )
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}


private void fastRemove(int index) {
    modCount  ;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index 1, elementData, index,
                numMoved);
    elementData[--size] = null; // Let gc do its work
}

通过remove方法删除成分最后是调用的fastRemove()方法,在fastRemove()方法中,首先对modCount进行加1操作(因为对聚焦修改了二次),然后接下去正是去除成分的操作,最终将size进行减1操作,并将援引置为null以福利垃圾搜集器进行回收专门的学业。

那么在乎此时各样变量的值:对于iterator,其expectedModCount为0,cursor的值为1,lastRet的值为0。

对于list,其modCount为1,size为0。

进而看程序代码,实行完删除操作后,继续while循环,调用hasNext方法()决断,由于此时cursor为1,而size为0,那么重返true,所以继续实施while循环,然后继续调用iterator的next()方法:

潜心,此时要专一next()方法中的第一句:checkForComodification()。

在checkForComodification方法中张开的操作是:

final void checkForComodification() {
    if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
}

如果modCount不等于expectedModCount,则抛出ConcurrentModificationException异常。

很鲜明,此时modCount为1,而expectedModCount为0,由在此以前后相继就抛出了ConcurrentModificationException格外。

到那边,想必大家应该掌握为啥上述代码会抛出ConcurrentModificationException极度了。

关键点就在于:调用list.remove()方法导致modCount和expectedModCount的值不平等。

留意,像使用for-each进行迭代实际上也会师世这种主题素材。

3.结语

在座职业后才意识,代码标准其实是非常重要的,高雅的代码便于明白和掩护,尤其节省时间。就好像经过一人的字就会大约看看那是二个什么样的人,代码亦是那般。刚结业不久的自家,更应当培育好的代码标准,那对今后的中年人必定是大有裨益的。

一.ConcurrentModificationException足够出现的缘由

2. 在单线程碰着下的消除办法

既是知道从头到尾的经过了,那么哪些缓慢解决吧?

实则异常粗略,留心的心上人或许开采在Itr类中也付出了四个remove()方法:

public void remove() {
    if (lastRet == -1)
        throw new IllegalStateException();
    checkForComodification();

    try {
       AbstractList.this.remove(lastRet);
        if (lastRet < cursor)
            cursor--;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException e) {
        throw new ConcurrentModificationException();
    }
}

在那一个艺术中,删除元素实际上调用的便是list.remove()方法,不过它多了贰个操作:

expectedModCount = modCount;

所以,在迭代器中若是要删减成分的话,要求调用Itr类的remove方法。

将上述代码改为上边那样就不会报错了:

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                iterator.remove();   //注意这个地方
        }
    }
}

先看上边这段代码:

3. 在二十多线程情状下的缓和情势

地点的消除办法在单线程境况下适用,可是在十六线程下适用吗?看上边三个事例:

public class Test {
    static ArrayList<Integer> list = new ArrayList<Integer>();
    public static void main(String[] args)  {
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        Thread thread1 = new Thread(){
            public void run() {
                Iterator<Integer> iterator = list.iterator();
                while(iterator.hasNext()){
                    Integer integer = iterator.next();
                    System.out.println(integer);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
        };
        Thread thread2 = new Thread(){
            public void run() {
                Iterator<Integer> iterator = list.iterator();
                while(iterator.hasNext()){
                    Integer integer = iterator.next();
                    if(integer==2)
                        iterator.remove(); 
                }
            };
        };
        thread1.start();
        thread2.start();
    }
}

运作结果:

图片 2

Muti-Thread ConcurrentModificationException

  有相当大恐怕有心上人说ArrayList是非线程安全的器皿,换来Vector就没难题了,实际上换到Vector依旧会现出这种不当。

原因在于,纵然Vector的秘籍运用了synchronized实行了一块儿,但是出于Vector是继续的AbstarctList,由此通过Iterator来访问容器的话,事实上是没有需求获得锁就足以访谈。那么鲜明,由于使用iterator对容器举办拜谒没有要求猎取锁,在二十四线程中就能够促成当一个线程删除了成分,由于modCount是AbstarctList的积极分子变量,由此或然会形成在任何线程中modCount和expectedModCount值不等。

就举例上边包车型客车代码中,很扎眼iterator是线程私有的,

初始时,线程1和线程2中的modCount、expectedModCount都为0,

当线程2通过iterator.remove()删除元素时,会修改modCount值为1,而且会修改线程第22中学的expectedModCount的值为1,

而此时线程第11中学的expectedModCount值为0,尽管modCount不是volatile变量,不保证线程1必将看收获线程2修改后的modCount的值,可是也会有相当大希望看得到线程2对modCount的修改,那样就有希望导致线程第11中学相比较expectedModCount和modCount不等,而抛出十一分。

因此平时有2种化解办法:

1)在行使iterator迭代的时候利用synchronized或许Lock实行联合;

2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。

转载自:http://www.cnblogs.com/dolphin0520/p/3933551.html

1

2

3

4

5

6

7

8

9

10

11

12publicclassTest {

publicstaticvoidmain(String[] args)  {

ArrayList list =newArrayList();

list.add(2);

Iterator iterator = list.iterator();

while(iterator.hasNext()){

Integer integer = iterator.next();

if(integer==2)

list.remove(integer);

}

}

}

运转结果:

图片 3

从这一个音信方可窥见,极度出现在checkForComodification()方法中。

大家不忙看checkForComodification()方法的切切实实贯彻,大家先依照程序的代码一步一步看ArrayList源码的落到实处:

先是看ArrayList的iterator()方法的有血有肉落到实处,查看源码开掘在ArrayList的源码中并未iterator()这些法子,那么很肯定这一个办法应该是其父类可能落成的接口中的方法,大家在其父类AbstractList中找到了iterator()方法的实际达成,上边是其落到实处代码:

1

2

3publicIterator iterator() {

returnnewItr();

}

从这段代码能够见见重返的是叁个针对Itr类型对象的援用,大家跟着看Itr的切切实实落到实处,在AbstractList类中找到了Itr类的切实可行落到实处,它是AbstractList的叁个分子内部类,上边这段代码是Itr类的装有实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39privateclassItrimplementsIterator {

intcursor =0;

intlastRet = -1;

intexpectedModCount = modCount;

publicbooleanhasNext() {

returncursor != size();

}

publicE next() {

checkForComodification();

try{

E next = get(cursor);

lastRet = cursor ;

returnnext;

}catch(IndexOutOfBoundsException e) {

checkForComodification();

thrownewNoSuchElementException();

}

}

publicvoidremove() {

if(lastRet == -1)

thrownewIllegalStateException();

checkForComodification();

try{

AbstractList.this.remove(lastRet);

if(lastRet < cursor)

cursor--;

lastRet = -1;

expectedModCount = modCount;

}catch(IndexOutOfBoundsException e) {

thrownewConcurrentModificationException();

}

}

finalvoidcheckForComodification() {

if(modCount != expectedModCount)

thrownewConcurrentModificationException();

}

}

率先大家看一下它的多少个成员变量:

cursor:表示下二个要探问的成分的目录,从next()方法的实际达成就可观察

lastRet:表示上多少个做客的因素的目录

expectedModCount:表示对ArrayList修改次数的愿意值,它的开首值为modCount。

modCount是AbstractList类中的三个分子变量

1

protectedtransientintmodCount =0;

该值表示对List的修改次数,查看ArrayList的add()和remove()方法就能够开掘,每一遍调用add()方法或许remove()方法就能够对modCount举行加1操作。

好了,到这里大家再看看上边包车型大巴次第:

当调用list.iterator()再次来到贰个Iterator之后,通过Iterator的hashNext()方法决断是还是不是还应该有成分未被访谈,大家看一下hasNext()方法,hashNext()方法的完成非常粗大略:

1

2

3publicbooleanhasNext() {

returncursor != size();

}

假设下一个拜望的因素下标不等于ArrayList的大小,就象征有成分须要拜会,那几个很轻易理解,假使下八个做客元素的下标等于ArrayList的大大小小,则确定达到最后了。

然后通过Iterator的next()方法取获得下标为0的因素,大家看一下next()方法的具体贯彻:

1

2

3

4

5

6

7

8

9

10

11publicE next() {

checkForComodification();

try{

E next = get(cursor);

lastRet = cursor ;

returnnext;

}catch(IndexOutOfBoundsException e) {

checkForComodification();

thrownewNoSuchElementException();

}

}

那边是非常重要的地点:首先在next()方法中会调用checkForComodification()方法,然后遵照cursor的值获取到成分,接着将cursor的值赋给lastRet,并对cursor的值实行加1操作。早先时,cursor为0,lastRet为-1,那么调用一遍以后,cursor的值为1,lastRet的值为0。注意此时,modCount为0,expectedModCount也为0。

随后往下看,程序中决断当前因素的值是不是为2,若为2,则调用list.remove()方法来删除该因素。

小编们看一下在ArrayList中的remove()方法做了何等:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26publicbooleanremove(Object o) {

if(o ==null) {

for(intindex =0; index < size; index )

if(elementData[index] ==null) {

fastRemove(index);

returntrue;

}

}else{

for(intindex =0; index < size; index )

if(o.equals(elementData[index])) {

fastRemove(index);

returntrue;

}

}

returnfalse;

}

privatevoidfastRemove(intindex) {

modCount ;

intnumMoved = size - index -1;

if(numMoved >0)

System.arraycopy(elementData, index 1, elementData, index,

numMoved);

elementData[--size] =null;// Let gc do its work

}

由此remove方法删除成分最终是调用的fastRemove()方法,在fastRemove()方法中,首先对modCount进行加1操作(因为对集中修改了一回),然后接下去正是去除成分的操作,最后将size实行减1操作,并将引用置为null以方便垃圾搜罗器举办回收专门的职业。

那就是说在乎此时逐个变量的值:对于iterator,其expectedModCount为0,cursor的值为1,lastRet的值为0。

对于list,其modCount为1,size为0。

紧接着看程序代码,试行完删除操作后,继续while循环,调用hasNext方法()判定,由于此时cursor为1,而size为0,那么重临true,所以继续推行while循环,然后继续调用iterator的next()方法:

瞩目,此时要留心next()方法中的第一句:checkForComodification()。

在checkForComodification方法中开展的操作是:

1

2

3

4finalvoidcheckForComodification() {

if(modCount != expectedModCount)

thrownewConcurrentModificationException();

}

如果modCount不等于expectedModCount,则抛出ConcurrentModificationException异常。

很显眼,此时modCount为1,而expectedModCount为0,由此先后就抛出了ConcurrentModificationException万分。

到这里,想必大家应该精通为何上述代码会抛出ConcurrentModificationException非常了。

关键点就在于:调用list.remove()方法导致modCount和expectedModCount的值分歧等。

只顾,像使用for-each实行迭代实际上也会师世这种主题素材。

二.在单线程蒙受下的消除办法

既然知道开始和结果了,那么如何消除呢?

其实很简短,留意的相恋的人大概开采在Itr类中也付出了四个remove()方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15publicvoidremove() {

if(lastRet == -1)

thrownewIllegalStateException();

checkForComodification();

try{

AbstractList.this.remove(lastRet);

if(lastRet < cursor)

cursor--;

lastRet = -1;

expectedModCount = modCount;

}catch(IndexOutOfBoundsException e) {

thrownewConcurrentModificationException();

}

}

在那个措施中,删除元素实际上调用的就是list.remove()方法,可是它多了两个操

1

expectedModCount = modCount;

故而,在迭代器中假如要刨除成分的话,供给调用Itr类的remove方法。

将上述代码改为上面这样就不会报错了:

1

2

3

4

5

6

7

8

9

10

11

12publicclassTest {

publicstaticvoidmain(String[] args)  {

ArrayList list =newArrayList();

list.add(2);

Iterator iterator = list.iterator();

while(iterator.hasNext()){

Integer integer = iterator.next();

if(integer==2)

iterator.remove();//注意那个地点

}

}

}

三.在十六线程景况下的解决方式

上边的消除办法在单线程情况下适用,不过在二十八线程下适用吗?看上边一个例证:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36publicclassTest {

staticArrayList list =newArrayList();

publicstaticvoidmain(String[] args)  {

list.add(1);

list.add(2);

list.add(3);

list.add(4);

list.add(5);

Thread thread1 =newThread(){

publicvoidrun() {

Iterator iterator = list.iterator();

while(iterator.hasNext()){

Integer integer = iterator.next();

System.out.println(integer);

try{

Thread.sleep(100);

}catch(InterruptedException e) {

e.printStackTrace();

}

}

};

};

Thread thread2 =newThread(){

publicvoidrun() {

Iterator iterator = list.iterator();

while(iterator.hasNext()){

Integer integer = iterator.next();

if(integer==2)

iterator.remove();

}

};

};

thread1.start();

thread2.start();

}

}

运营结果:

图片 4

有不小可能率有意中人说ArrayList是非线程安全的容器,换到Vector就没难点了,实际上换来Vector如故会现出这种错误。

原因在于,尽管Vector的不二诀要运用了synchronized进行了一齐,可是由于Vector是三翻五次的AbstarctList,因此通过Iterator来访问容器的话,事实上是不供给得到锁就能够访谈。那么分明,由于应用iterator对容器实行探问没有供给获得锁,在多线程中就能够促成当一个线程删除了成分,由于modCount是AbstarctList的分子变量,由此可能会导致在任何线程中modCount和expectedModCount值不等。

就比如上边的代码中,很猛烈iterator是线程私有的,

初始时,线程1和线程2中的modCount、expectedModCount都为0,

当线程2通过iterator.remove()删除成分时,会修改modCount值为1,并且会修改线程第22中学的expectedModCount的值为1,

而此刻线程第11中学的expectedModCount值为0,即便modCount不是volatile变量,不保障线程1势必须要看收获线程2修改后的modCount的值,然而也会有望看获得线程2对modCount的修改,那样就有望形成线程第11中学相比较expectedModCount和modCount不等,而抛出十二分。

从而日常有2种化解办法:

1)在行使iterator迭代的时候利用synchronized也许Lock实行协同;

2)使用并发容器CopyOnWriteArrayList替代ArrayList和Vector。

至于并发容器的从头到尾的经过就要下一篇著作中描述。

参照他事他说加以考察资料:

http://blog.csdn.net/izard999/article/details/6708738

http://www.2cto.com/kf/201403/286536.html

TAG标签:
版权声明:本文由澳门新葡8455手机版发布于计算机编程,转载请注明出处:AlibabaJava开采手册