文章目录
ArrayList
问题的出现
案例1
public class SingletonDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int j = 0; j <30 ; j++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8).toString());
list.forEach(o -> System.out.println(o));
}).start();
}
}
}
出现问题:
java.util.ConcurrentModificationException
在《java并发程序设计》一书也提到一个例子
案例2
public class SingletonDemo {
static List arrayList = new ArrayList();
public static void main(String[] args) {
new Thread(()->{
for (int i = 0; i<1000 ; i++) {
arrayList.add(i);
}
},"t1").start();
new Thread(()->{
for (int i = 0; i<1000 ; i++) {
arrayList.add(i);
}
},"t2").start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(arrayList.size());
}
}
有三个情况 size = 2000
java.lang.ArrayIndexOutOfBoundsException
size<2000
本书中给的建议,是使用Vector但是并不好,我下面我会给更好建议.
为什么会出现这个问题
假如我们举个例子,每来一个人进行签到,加入我们加锁,有人维持秩序,此时不允许代签,但是如果没有加锁,此时张三刚写上自己的名字的姓,还没有写完,另一个线程直接推开张三,打断了这个写的操作,此时就是并发修改异常.
刚开始的时候,所有的线程的引用对象为空的签名单,此时A签名签完后,告诉其他线程,你们别用过去没有我名字的签名单,你们应该引用有我名字的签名单,然后才能继续签名.
解决方案
1:使用Vector,但是性能会急剧下降
2:使用Collections工具类,同理后面相关的集合如map 也可以使用相同的办法,后面不在陈述.
public class SingletonDemo {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
List<String> list = Collections.synchronizedList(strings);
for (int j = 0; j <30 ; j++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8).toString());
list.forEach(o -> System.out.println(o));
}).start();
}
}
}
3:使用JUC的并发类 CopyOnWriteArrayList (核心)
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();
}
为什么CopyOnWriteArrayList可以解决
1:首先定义的数组,加上了volatile (如果对volatile的作用和底层不熟悉的同学,可以看看我写博客https://william.blog.csdn.net/article/details/102870414)
2:add核心源码
*/
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();
}
}
HashSet
问题的出现
public class SingletonDemo {
public static void main(String[] args) {
Set list = new HashSet<>();
for (int j = 0; j <30 ; j++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8).toString());
list.forEach(o -> System.out.println(o));
}).start();
}
}
}
出现的问题:java.util.ConcurrentModificationException
HashSet结构
面试的时候会有这样的场景
面试官: HashSet的底层是什么实现
我: HashMap
面试官:HashMap是key和value键值对的,为什么HashSet只添加一个值?
源码解决
可以看到hashset只在乎他的key,他的value为一个常量对象.
解决办法
这里直接将核心的,其他的解决思路和arraylist基本思想一样
使用CopyOnWriteArraySet
public class SingletonDemo {
public static void main(String[] args) {
Set list = new CopyOnWriteArraySet();
for (int j = 0; j <30 ; j++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8).toString());
list.forEach(o -> System.out.println(o));
}).start();
}
}
}
为什么CopyOnWriteArraySet可以解决
1:可以看到CopyOnWriteArraySet底层实际上使用还是CopyOnWriteArrayList
Map
问题的出现
public class SingletonDemo {
public static void main(String[] args) {
Map map = new HashMap();
for (int j = 0; j < 30; j++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8).toString());
System.out.println(map);
}).start();
}
}
}
出现的问题:ConcurrentModificationException
解决办法
你是不是还以为有一个CopyOnWriteHashMap? 但是并没有,他为我们提供了ConcurrentHashMap
public class SingletonDemo {
public static void main(String[] args) {
Map map = new ConcurrentHashMap();
for (int j = 0; j < 30; j++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8).toString());
System.out.println(map);
}).start();
}
}
}