A、リスト
1、コードが示します
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
2、症状
java.util.ConcurrentModificationExceptionが
3、原因
スレッドは、上異常な同時修飾によって引き起こされる矛盾したデータ、その結果、別のスレッドを奪うために、書き込みを行っています
4.ソリューション
- 新しいベクトル<>()
- Collections.synchronizedList()
- 新しいCopyOnWriteArrayListと<>()
このクラスは推奨されCopeOnWriteArrayListは、書き込みにどのくらいの時間を読んで
コピーを書くとき、分離のメリットの思想を読み書き:読んで完全にロック
使用シナリオ:書き込みは非常に少数の機会には、短期的な一貫性のない読み取りと書き込みを許容することができます。
イテレータは、読み取り専用でCopyOnWriteArrayListと、追加および削除をサポートしていません。
5、CopyOnWriteArrayListとソースコードの解釈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
二、セット
1、コードは示しています。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
2、解決策:
- Collections.synchronizedSet()
- 新しいCopyOnWriteArraySet <>()
3、CopyOnWriteArraySet基礎となるソースコード:
下CopyOnWriteArrayListとを使用してください
1 2 3 |
|
4、HashSetの基礎となるソース
HashSetのキーはあなたが(付加価値である)、値が唯一のキーHashSetのに興味を持っているPRESENTオブジェクトタイプと呼ばれる定数であり、さ
1 2 3 4 5 6 7 8 9 10 |
|
三、地図
1、コードは示しています。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
2.ソリューション
- Collections.synchronizedMap()
- 新しいのConcurrentHashMap <>();
参考:https://www.cnblogs.com/wjh123/p/11259409.html
私たちは、セットはスレッドセーフではないことを、安全でないコーディングケースを書いてください知っているとソリューションを提供しますか?
1、Set线程不安全问题产生
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class ContainerNotSafeDemoTwo {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString());
System.out.println(set.toString());
}, "T1").start();
}
}
}
程序执行结果如下:报java.util.ConcurrentModificationException异常
2、ConcurrentModificationException产生的原因
一个线程正在写,另一个线程过来抢占资源,会造成数据不一致,进而报并发修改异常。
3、Set线程不安全解决方案
(1)第一种解决方案
使用Collections工具类创建同步集合类
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class ContainerSafeSetDemoOne {
public static void main(String[] args) {
Set<String> set = Collections.synchronizedSet(new HashSet<>());
for (int i = 0; i < 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString());
System.out.println(set.toString());
}, String.valueOf(i)).start();
}
}
}
(2)第二种解决方案
使用并发编程类CopyOnWriteArraySet替换HashSet
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class ContainerSafeSetDemoTwo {
public static void main(String[] args) {
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString());
System.out.println(set.toString());
}, String.valueOf(i)).start();
}
}
}
CopyOnwriteArraySet容器即写时复制容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object []进行copy,复制出一个新的容器object[] newElements,然后往新的容器Object [] newElements里添加元素,添加元素之后,再将原容器的引用指向新的容器setArray(newElements);这样做的好处是可以对CopyOnWrite容器进行并发读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写是不同的容器。
public boolean add(E e) {
return al.addIfAbsent(e);
}
/**
* Appends the element, if not present.
*
* @param e element to be added to this list, if absent
* @return {@code true} if the element was added
*/
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
addIfAbsent(e, snapshot);
}
/**
* A version of addIfAbsent using the strong hint that given
* recent snapshot does not contain e.
*/
private boolean addIfAbsent(E e, Object[] snapshot) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] current = getArray();
int len = current.length;
if (snapshot != current) {
// Optimize for lost race to another addXXX operation
int common = Math.min(snapshot.length, len);
for (int i = 0; i < common; i++)
if (current[i] != snapshot[i] && eq(e, current[i]))
return false;
if (indexOf(e, current, common, len) >= 0)
return false;
}
Object[] newElements = Arrays.copyOf(current, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
参考:https://blog.csdn.net/longgeqiaojie304/article/details/89819422
集合类不安全操作:只要是在Collections挂过号的都是线程不安全的:
java.util.ConcurrentModificationException:并发修改异常
四、我们知道ArraysList是线程不安全的,请编写一个不安全的案例并给出解决方案:
1. ArraysList:
问题:
1. 当new一个ArrayList的时候底层是啥?
数组;
2. 什么类型的数组?
泛型定义的类型,如果没定义就是一个空的,默认长度为10的Object数组;
3. 扩容:
需要长度大于原数组长度,大小扩大到原值1.5倍,将老数组copy到新数组
4. ArrayList是线程安全的还是线程不安全的?
线程不安全
5. 给我举个线程不安全的例子
java.util.ConcurrentModificationException:并发修改异常
package com.example.code.conllection; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; /** * 1. 故障现象:java.util.ConcurrentModificationException * * 2. 导致原因: * 并发争抢导致,参考花名册签到,一个人正在写入,另一个同学过来抢夺,导致数据不一致,并发修改异常; * 3. 解决方案: * - 用Voctor:数据一致性绝对可以保证,但数据安全性几句下降 * - Collections.synchronizedList(new ArrayList<>()):Collections工具类 * - java.util.concurrent.CopyOnWriteArrayList:写时复制 * 4. 优化建议(同样的错误不犯第2次) */ public class ContainerNotSafeDemo { public static void main(String[] args) { List<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 30; i++) { new Thread(() -> { list.add(UUID.randomUUID().toString().substring(0, 8)); System.out.println(list); },String.valueOf(i)).start(); } } } /** * 写时复制: * 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(); } } */
2. HashSet:
问题:
1. HashSet底层是什么?
创建了一个初始值是16,负载因子0.75的标准HashMap
2. 你确定HashSet的底层是HashMap吗?HashSet.add加值的时候加一个,而HashMap需要两个k-v键值对?
HashSet底层确实是HashMap,HashSet.add的时候确实调用的是map.put方法,add传入法人值就是map中的key,而value是Object的常量
package com.example.code.conllection; import java.util.HashSet; import java.util.Set; import java.util.UUID; /** * HashSet * 1. 故障现象:java.util.ConcurrentModificationException * 2. 导致原因: * 并发争抢导致,参考花名册签到,一个人正在写入,另一个同学过来抢夺,导致数据不一致,并发修改异常; * 3. 解决方案: * - Collections.synchronizedSet(new HashSet<>()); * - new CopyOnWriteArraySet<>():底层还是CopyOnWriteArrayList * 4. 优化建议(同样的错误不犯第2次) * */ public class CurrentSetDemo { public static void main(String[] args) { Set<String> list = new HashSet<>(); for (int i = 0; i < 30; i++) { new Thread(() -> { list.add(UUID.randomUUID().toString().substring(0, 8)); System.out.println(list); },String.valueOf(i)).start(); } } }
3. HashMap:
问题:
1. HashMap的实现原理?
2. ConcurrentHashMap的特点?
package com.example.code.conllection; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; /** * HashMap * 1. 故障现象:java.util.ConcurrentModificationException * 2. 导致原因: * 并发争抢导致,参考花名册签到,一个人正在写入,另一个同学过来抢夺,导致数据不一致,并发修改异常; * 3. 解决方案: * - Collections.synchronizedMap(new HashMap<>()); * - new ConcurrentHashMap<>(); * 4. 优化建议(同样的错误不犯第2次) * */ public class CurrentMapDemo { public static void main(String[] args) { Map<String, String> map = new ConcurrentHashMap<>(); for (int i = 0; i < 30; i++) { new Thread(() -> { map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8)); System.out.println(map); },String.valueOf(i)).start(); } } }
参考:https://www.cnblogs.com/luliang888/p/12105339.html