集合
在高并发情况下,我们熟知的ArrayList,HashMap等集合类都是线程不安全的,
代码测试:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i=0;i <= 10; i++){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
List 不安全
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
展示一段ArrayList的源码 ,在进行add操作是,他没有进行加锁的处理,在并发的情况下,他的取值是不一致的,会触发ConcurrentModificationException,并发修改异常!
熟知Collections的人就会清楚,这种情况下,有几种解决方法
1.使用Vector,他是线程安全的
2.使用Collections.synchronizedList(List list)
在JUC包下,CopyOnWriteArrayList是一种好的方式
他是一个线程安全的变体ArrayList ,其中所有可变操作( add , set ,等等)通过对底层数组的最新副本实现。
看看他的add方法
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#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();
}
}
然后在看看set,其实和list差不多
Set<String> set = new HashSet<>();
for (int i = 1;i <= 30 ;i++){
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
add方法set 的本质就是map,key是无法重复的!
public static void main(String[] args) {
// Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1;i <= 30 ;i++){
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
HahsMap
照样 看源码
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
这几个集合类的解决方法大同小异,
public static void main(String[] args) {
Map<String, String> map = new ConcurrentHashMap<>();
// Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
for (int i =1;i <=100;i++){
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
这几个在JUC包中的集合类,都是线程安全的,他们在进行一些数据的增加删除,都进行了锁的处理,在写的时候,加锁,只允许一个人来写,在读的时候,共享,允许多人来读。