package com.thread_test;
import java.util.ArrayList;
import java.util.Iterator;
/**
* Created by zhen on 2017-06-09.
*/
public class SynchronizedCollectionTest {
/**
* 传统线程在并发访问时的问题
hashMap是线程不安全的.可以使用hashMap时加上同步解决。
jdk1.5之前的解决方法是使用Collections.synchronizedMap()方法得到同步的Map。
这个是怎么实现的呢?可以看看源码:
返回一个synchronizedMap对象,观察synchronizedMap类:
private static class SynchronizedMap<K,V>
implements Map<K,V>, Serializable {
private static final long serialVersionUID = 1978198479659022715L;
private final Map<K,V> m; // Backing Map
final Object mutex; // Object on which to synchronize
SynchronizedMap(Map<K,V> m) {
this.m = Objects.requireNonNull(m);
mutex = this;
}
SynchronizedMap(Map<K,V> m, Object mutex) {
this.m = m;
this.mutex = mutex;
}
public int size() {
synchronized (mutex) {return m.size();}
}
public boolean isEmpty() {
synchronized (mutex) {return m.isEmpty();}
}
public boolean containsKey(Object key) {
synchronized (mutex) {return m.containsKey(key);}
}
public boolean containsValue(Object value) {
synchronized (mutex) {return m.containsValue(value);}
}
public V get(Object key) {
synchronized (mutex) {return m.get(key);}
}
public V put(K key, V value) {
synchronized (mutex) {return m.put(key, value);}
}
public V remove(Object key) {
synchronized (mutex) {return m.remove(key);}
}
public void putAll(Map<? extends K, ? extends V> map) {
synchronized (mutex) {m.putAll(map);}
}
public void clear() {
synchronized (mutex) {m.clear();}
}
private transient Set<K> keySet;
private transient Set<Map.Entry<K,V>> entrySet;
private transient Collection<V> values;
public Set<K> keySet() {
synchronized (mutex) {
if (keySet==null)
keySet = new SynchronizedSet<>(m.keySet(), mutex);
return keySet;
}
}
。。。
这尼玛,代理。有点装饰模式的样子,是你,有你增强你。
在并发状态,可以使用concurrentHashMap替代hashMap。concurrentSkipListMap是可以排序的安全map,类似treeMap
我们的ArrayList等简单几何还有一些问题,比如在遍历的时候删除:
当下面的元素删除1的时候抛出异常:版本修改异常
删除2的时候却没有问题
删除3的时候抛出版本修改异常
这是为什么呢?
我们查看源码:
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
modCount != expectedModCount;
查看代码:
刚开始 int expectedModCount = modCount;
进行集合元素的修改的时候,会将modCount++
然后在next()方法中调用进行判断,这个版本有点事乐观锁的概念,我隐约听过这个概念。
就是在next()方法内抛出的异常。所以1,和3的删除都抛出了版本修改异常。
为什么删除2的时候不抛异常呢?
因为在next()之前我们调用了hasNext()方法,它比较cursor和size。如果相等则返回false,否则返回true。2的时候刚好集合的大小与指针的位置大小相等,所以不往下走了。
删除3的时候cousor与size永远不可能相等了,所以hasNext永远返回true。好在next()方法中有检查版本,所以抛出了异常
jdk5的线程安全类不仅解决了安全问题,将这个问题也一并解决了。
CopyOnWriteArrayList 和 CopyOnWriteArraySet 可以替换我们常用的ArrayList和HashSet。
如果Set要排序使用ConcurrentSkipListSet
hashSet本身是内置一个hashMap,就是一个hashMap的key的集合。
*/
public static void main(String[] args){
ArrayList<String> users = new ArrayList<String>();
users.add("1");
users.add("2");
users.add("3");
Iterator<String> iterator = users.iterator();
while(iterator.hasNext()){
String obj = iterator.next();
if(obj.equals("1")){
users.remove(obj);
}else{
System.out.println(obj);
}
}
}
}