不安全的案例
额,这里我踩坑了,在Junit中启动编写启动,一直启动不起来,后面百度才知道Junit中不能启动多线程(详细的可以去见这个https://blog.csdn.net/qq_40889460/article/details/101192003)
System.out.println("----------线程不安全案例-----------");
List<String> list = new ArrayList<>();
for (int i = 0; i < 3000; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,10));
System.out.println(list);
},String.valueOf(i)).start();
}
出现了错误,并发修改错误
java.util.ConcurrentModificationException
解决方案
一、使用Vector (不推荐,性能太差)本质就是通过加上锁
System.out.println("----------解决方案-----------");
//一、vector
List<String> list = new Vector<>();
for (int i = 0; i < 3000; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,10));
System.out.println(list);
},String.valueOf(i)).start();
}
查看vector源码 方法加上synchronized 性能很差
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
二、第二种,使用工具类Collections包装ArrayList
//2、使用Collections工具类包装Arraylist
List<String> list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 3000; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,10));
System.out.println(list);
},String.valueOf(i)).start();
}
查看了Collections.synchronizedList()方法,将ArrayList包装,生成一个SynchronizedList对象
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
查看SynchronizedList里面的add方法
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
本质的解决方案 还是依赖于synchronized 在代码块中加锁,相对于Vector在方法上加锁,锁的范围小,性能会高一点
三、采用读写分离思想
//3、采用读写分离思想 (性能最佳),写时复制
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 3000; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,10));
System.out.println(list);
},String.valueOf(i)).start();
}
读写分离 :就是修改list的时候,是拷贝一份进行修改,其他的读 还是读原来的那一份,等写完,将原来那份失效
查看了CopyOnWriteArrayList 的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();
}
}
读的时候 是不用加锁的
public E get(int index) {
return get(getArray(), index);
}
对于其他的Map,Set 并发修改问题,都是采用一样的方案去解决