1、证明ArrayList线程不安全
class ListNoSafe{
private List<String> list = new ArrayList<>();
public void add(){
list.add(UUID.randomUUID().toString().substring(0,3));
System.out.println(list);
}
}
public class ThreadArrayList {
public static void main(String[] args) {
ListNoSafe listNoSafe = new ListNoSafe();
for (int i = 0; i < 5; i++) {
new Thread(()->{
for (int j = 0; j < 5; j++) {
listNoSafe.add();
}
},String.valueOf(i)).start();
}
}
}
=========或者直接如下=========
List<String> list = new ArrayList<>();
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();
}
多次运行会抛出java.util.ConcurrentModificationException异常
分析ArrayList源码
//可知其是线程不安全的
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
2、使用Vector
Vector源码
//线程安全的
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
修改代码为
List<String> list = new Vector();
3、使用Collections
private List<String> list = Collections.synchronizedList(new ArrayList<>());
4、使用JUC包下的方法 - - 写时复制
List<String> list = new CopyOnWriteArrayList<>();
5、写时复制
首先因为为了保证线程安全,会采用加锁进行解决,但是加锁会导致性能下降
-
CopyOnWriteArrayList定义
CopyOnWriteArrayList是arraylist的一种线程安全变体,
其中所有可变操作(add、set等)都是通过生成底层数组的新副本来实现的。
5.1、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();
}
}
CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接在往当前容器Object[]添加,而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素
添加元素后,再将原容器的引用指向新的容器setArray(newElements),这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何的元素,所以CopyOnWrite容器也是一种读写分离的思想,读和写在不同的容器
6、HashSet
首先HashSet底层数据结构是HashMap,往HashSet添加元素,存放在K中,在V中保存常量Object对象
public HashSet() {
map = new HashMap<>();
}
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
线程
Set<String> set = new HashSet<>();//线程不安全
Set<String> set = new CopyOnWriteArraySet<>();//线程安全
7、HashMap
线程
Map<String,String> map = new HashMap<>();//线程不安全
Map<String,String> map = new ConcurrentHashMap<>();//线程安全
8、代码
/**
* 请举例说明集合类是不安全的
*/
public class NotSafeDemo {
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();
}
}
private static void setNoSafe() {
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i <30 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(set);
},String.valueOf(i)).start();
}
}
private static void listNoSafe() {
// List<String> list = Arrays.asList("a","b","c");
// list.forEach(System.out::println);
//写时复制
List<String> list = new CopyOnWriteArrayList<>();
// new CopyOnWriteArrayList<>();
//Collections.synchronizedList(new ArrayList<>());
//new Vector<>();//new ArrayList<>();
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();
}
}
}