这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战」
安全集合
对于需要进行多线程操作的集合来说,使用普通的单线程的集合存储数据,就可能会出现线程安全问题,下面来介绍常见的多线程集合.
list
当多线程向list中插入值时,会报错。因为ArrayList是线程不安全的
ArrayList< String > strings = new ArrayList<>();
// 创建10个线程同时插入list
for (int i = 0; i < 100 ; i++) {
new Thread(()->{
strings.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(strings);
},String.valueOf(i)).start();
}
解决方法
1.使用vector。vector是线程安全,可以解决这个问题。
在源码中,add增加了syn关键字
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();
}
}
2.使用Collections 方法类的转换成synchronizedList方法。可以将ArrayList转换成线程安全的list
给该list加上了一mutex的Object锁
List<String> strings = Collections.synchronizedList(new ArrayList<>());
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
3.使用CopyOnWriteArrayList<>() ;
写入时复制.是一种优化策略. 多线程读取时,固定.写入时,先复制在写入再返回,避免覆盖
源码中使用lock锁。 所以
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)
// 写的时候copyOf
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}
set
set和list同理
1.Collections.Collections.synchronizedSet
2.new CopyOnWriteArraySet()
hashSet就是HashMap,但是添加的时候key不能相同
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
map
hashMap:创建时默认加载因子0.75,初始容量16
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// 最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
1.hashTable ,使用syn关键字 ,所以效率低。不允许key和value为null
public synchronized V put(K key, V value) {
1.Collections.synchronizedMap
2.new ConcurrentHashMap<>()
// 初始容量
private static final int DEFAULT_CAPACITY = 16;
// 默认并发数量
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
concurrentHashMap
jdk1.7中,底层通过lock+segment+hashEntry 实现
该map由多个segment组成,segment集成lock