集合类不安全之并发修改异常
问题:我们知道ArrayList是线程不安全,请编码写一个不安全的案例并给出解决方案。
package ArrayList;
import java.util.ArrayList;
/*
* 集合类不安全的问题
* ArrayList
* */
public class ContainerNotSafeDemo {
public static void main(String[] args) {
//ArrayList是对数组的包装
//第一问当我new一个 ArrayList,底层是什么?--------数组
//第二问什么类型的数组?--------这里为整型 <泛型>(当你new的时候,Constructs an empty list with an initial capacity of ten.)
new ArrayList<Integer>();
}
}
接着看elementData是什么?
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
是一个 Object数组
transient Object[] elementData;
列表为什么为空?
因为DEFAULTCAPACITY_EMPTY_ELEMENTDATA为空
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
当add添加1
public class ContainerNotSafeDemo {
public static void main(String[] args) {
//ArrayList是对数组的包装
//第一问当我new一个 ArrayList,底层是什么?--------数组
//第二问什么类型的数组?--------这里为整型(当你new的时候,Constructs an empty list with an initial capacity of ten.)
new ArrayList<Integer>().add(1);
}
这时size+1,而size初始值为0.
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
如果超过10,需要扩容。
举例----单线程程比较安全
一般不会出错
package ArrayList;
import java.util.ArrayList;
import java.util.List;
/*
* 集合类不安全的问题
* ArrayList
* */
public class ContainerNotSafeDemo {
public static void main(String[] args) {
//ArrayList是对数组的包装
//第一问当我new一个 ArrayList,底层是什么?--------数组
//第二问什么类型的数组?--------这里为整型(当你new的时候,Constructs an empty list with an initial capacity of ten.)
// new ArrayList<Integer>().add(1);
List<String> list=new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
for (String element : list){
System.out.println(element);
}
}
}
结果
a
b
c
Process finished with exit code 0
多线程
public class ContainerNotSafeDemo {
public static void main(String[] args) {
//ArrayList是对数组的包装
//第一问当我new一个 ArrayList,底层是什么?--------数组
//第二问什么类型的数组?--------这里为整型(当你new的时候,Constructs an empty list with an initial capacity of ten.)
// new ArrayList<Integer>().add(1);
List<String> list=new ArrayList<>();
//多线程
for (int i = 1; i <= 3; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
结果不正确,但是没报错,证明了线程不安全
[null, 244aa3b3]
[null, 244aa3b3, 3a1a3d91]
[null, 244aa3b3]
Process finished with exit code 0
当 i<=30时
结果
"C:\Program Files\Java\jdk1.8.0_144\bin\java.exe" "-javaagent:F:\idea2019\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar=49644:F:\idea2019\IntelliJ IDEA 2019.1.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;F:\039_面试视频\笔记\out\production\笔记;F:\039_面试视频\笔记\lib\lombok-1.16.20.jar" ArrayList.ContainerNotSafeDemo
[e9b0e33b, ec95100c]
[e9b0e33b, ec95100c, 15ae19fc]
Exception in thread "14" [e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2]
Exception in thread "28" [e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177]
Exception in thread "23" [e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292]
Exception in thread "17" Exception in thread "15" [e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849]
Exception in thread "3" [e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849, c77fdb58, 37efc073, 31ed5bbb]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849, c77fdb58, 37efc073, 31ed5bbb, 3363a98a, 599149d9]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849, c77fdb58]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849, c77fdb58, 37efc073, 31ed5bbb, 3363a98a, 599149d9, 3e15749c, e8351919]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849, c77fdb58, 37efc073, 31ed5bbb, 3363a98a, 599149d9, 3e15749c, e8351919, a1f02e5a]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849, c77fdb58, 37efc073, 31ed5bbb, 3363a98a, 599149d9, 3e15749c, e8351919, a1f02e5a, e445a60c]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849, c77fdb58, 37efc073, 31ed5bbb, 3363a98a, 599149d9, 3e15749c, e8351919, a1f02e5a, e445a60c, eb5bfb7c]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849, c77fdb58, 37efc073, 31ed5bbb, 3363a98a, 599149d9, 3e15749c, e8351919, a1f02e5a, e445a60c, eb5bfb7c, e5d9be52]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849, c77fdb58, 37efc073, 31ed5bbb, 3363a98a, 599149d9, 3e15749c]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849, c77fdb58, 37efc073, 31ed5bbb, 3363a98a, 599149d9, 3e15749c, e8351919, a1f02e5a, e445a60c, eb5bfb7c, e5d9be52, 9a1301d0]
[e9b0e33b, ec95100c, 15ae19fc, 9b4b0406, 67a91cb2, 97b61c27, 4d7ae7fc, 9e3c5177, bc7d6d89, 5ab2c07c, 593711f3, 1fad7d27, 1acc8cd2, decac026, 4b899326, 23c0c292, 22d8ff11, 9c663849, c77fdb58, 37efc073, 31ed5bbb, 3363a98a]
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at ArrayList.ContainerNotSafeDemo.lambda$main$0(ContainerNotSafeDemo.java:32)
at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at ArrayList.ContainerNotSafeDemo.lambda$main$0(ContainerNotSafeDemo.java:32)
at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at ArrayList.ContainerNotSafeDemo.lambda$main$0(ContainerNotSafeDemo.java:32)
at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at ArrayList.ContainerNotSafeDemo.lambda$main$0(ContainerNotSafeDemo.java:32)
at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at ArrayList.ContainerNotSafeDemo.lambda$main$0(ContainerNotSafeDemo.java:32)
at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at ArrayList.ContainerNotSafeDemo.lambda$main$0(ContainerNotSafeDemo.java:32)
at java.lang.Thread.run(Thread.java:748)
Process finished with exit code 0
其中 java.util.ConcurrentModificationException 是ArrayList线程不安全在高并发多线程访问下面的异常。
Concurrent-----并发
Modification----修改
ConcurrentModificationException -----并发修改的异常
解决的例子
1.线程安全用 Vector<>
线程不安全,不考虑用性能的ArrayList
List<String> list= new Vector<>();
2.其中了解collection、collections区别,前者为接口,后者为类
List<String> list=Collections.synchronizedList(new ArrayList<>());
3.CopyOnWriteArrayList<>();
List<String> list= new CopyOnWriteArrayList<>();
1.故障现象
Java.util.ConcurrentModificationException
2.导致原因
并发争抢修改导致,参考花名册签名情况。
一个人正在写入,另一个人过来抢夺,导致数据不一致异常。并发修改异常。
3.解决方案
3.1 new Vector<>();----------(List<String> list=new Vector<>();)
3.2 Collections.synchronizedList(new ArrayList<>());
3.3 new CopyOnWriteArrayList<>();
4.优化建议(同样的错误不犯第二次)
笔记
写实复制
CopyOnWrite容器即写实复制的容器。往一个容器添加元素的时候,不直接往当前容器object【】添加,而是先将当前容器object【】进行copy,复制出一个新的容器object【】 newElements,然后新的容器object【】newElements里添加元素,添加完元素之后,再将原容器的引用指向新的容器setArray(newElements);这样做的好处是可以对copyonWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以copyonWrite容器也是一种读写分离的思想,读和写不同的容器
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}