Java 集合框架04-fail-fast总结

网友投稿 694 2022-05-30

上一篇我们介绍了ArrayList的相关源码,这篇我们将了解一下fail-fast机制的相关知识

fail-fast的简介

fail-fast的相关示例

fail-fast的解决办法

fail-fast的原理

解决fail-fast的原理

fail-fast的简介

fail-fast机制是java 集合的一种错误机制。 当多个线程对同一个集合的内容进行操作时,就会产生fail-fast事件。

例如:当线程A通过iterator迭代器(或者 For和RandomAccess)来访问集合时,线程B对集合的内容进行了修改,则线程A在访问集合时就会抛出ConcurrentModificationException异常,产生fail-fast事件。

fail-fast的相关示例

package com.jay.collection; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Created by xiang.wei on 2018/3/4 * * @author xiang.wei */ public class FailFastTest { /** * */ private static List globalList = new ArrayList(); /** * 线程one,two,three同时启动,线程one向globalList中添加元素0,1,2,3,4,5,然后遍历打印 * 线程two 与此同时也向globalList中添加元素10,11,12,13,14,15,然后遍历打印, * 线程three 同时也在移除globalList中元素0,1,2,3,4,5,然后遍历打印 * */ public static void main(String[] args) { new ThreadOne().start(); new ThreadTwo().start(); new ThreadThree().start(); } /** * 向globalList中添加元素0,1,2,3,4,5,然后遍历打印 */ private static class ThreadOne extends Thread { @Override public void run() { for (int i = 0; i < 6; i++) { globalList.add(String.valueOf(i)); printAll(globalList); } } } /** * 向globalList中添加元素10,11,12,13,14,15,然后遍历打印 */ private static class ThreadTwo extends Thread { @Override public void run() { for (int i = 10; i < 16; i++) { globalList.add(String.valueOf(i)); printAll(globalList); } } } /** * 移除globalList中元素0,1,2,3,4,5,然后遍历打印 */ private static class ThreadThree extends Thread { @Override public void run() { for (int i = 0; i < 6; i++) { globalList.remove(String.valueOf(i)); printAll(globalList); } } } private static void printAll(List testList) { Iterator testIter = testList.iterator(); while (testIter.hasNext()) { System.out.println((testIter.next())); } } }

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

Java 集合框架04-fail-fast总结

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

fail-fast的解决办法

按照Java api文档的建议,在多线程环境中我们需要选用java.util.concurrent包下的CopyOnWriteArrayList集合,

即将

private static List globalList = new ArrayList();

1

替换成

private static List globalList = new CopyOnWriteArrayList();

1

fail-fast的原理

我们此处以ArrayList为例,其Iterator的实现是在AbstractList中

private class Itr implements Iterator { int cursor = 0; int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) 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(); }

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

39

40

首先,定义一个实例变量

protected transient int modCount = 0;

1

2

当实例化一个迭代器Itr时,会将modCount的值赋给expectedModCount,以后每次迭代时都会检查这两个值是否相等,如果不相等则会抛出ConcurrentModificationException。显然,expectedModCount不会改变,那么哪些操作会修改modCount的值呢?

(ps: 这也解释了为啥iterator为啥可以直接移除当前元素)

public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { //增加modCount的值 modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } public E remove(int index) { rangeCheck(index); //增加modCount的值 modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }

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

从上述源码中我们可以看出 add,remove方法修改modCount的值。

解决fail-fast的原理

我们接着看看CopyOnWriteArrayList是如何解决fail-fast的问题的。

首先CopyOnWriteArrayList的定义

public class CopyOnWriteArrayList implements List, RandomAccess, Cloneable, java.io.Serializable {}

1

2

CopyOnWriteArrayList直接实现了List,其iterator的迭代器COWIterator是在自身类实现的。

static final class COWIterator implements ListIterator { private final Object[] snapshot; /** Index of element to be returned by subsequent call to next. */ private int cursor; private COWIterator(Object[] elements, int initialCursor) { cursor = initialCursor; snapshot = elements; } public boolean hasNext() { return cursor < snapshot.length; } public boolean hasPrevious() { return cursor > 0; } @SuppressWarnings("unchecked") public E next() { if (! hasNext()) throw new NoSuchElementException(); return (E) snapshot[cursor++]; }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

(01) 和ArrayList继承于AbstractList不同,CopyOnWriteArrayList没有继承于AbstractList,它仅仅只是实现了List接口。

(02) ArrayList的iterator()函数返回的Iterator是在AbstractList中实现的;而CopyOnWriteArrayList是自己实现Iterator。

(03) ArrayList的Iterator实现类中调用next()时,会“调用checkForComodification()比较‘expectedModCount’和‘modCount’的大小”;但是,CopyOnWriteArrayList的Iterator实现类中,没有所谓的checkForComodification(),更不会抛出ConcurrentModificationException异常!

引用

http://www.cnblogs.com/skywang12345/p/3308762.html

Java 任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:CSS使用集群快照跨region复制指导
下一篇:单元测试新手上路——个人实践总结
相关文章