一 概述

二 Vector
Vector线程安全的集合类,同时它是基于动态数组实现的集合类,借助Synchronized修饰的方法来保证多线程安全。在Vector集合类中Synchronized加在方法上,性能会比较差。
三 通过Collections中的指定方法保证线程安全
ArrayList和HashMap为线程不安全的集合类,我们可以通过Collections中提供的方法使得其能够在多线程的情况下保持线程安全。
Collections.synchronizedList(new ArrayLixt<E>)
//借助同步锁synchronized (mutex)声明同步代码块实现多线程安全
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
SynchronizedRandomAccessList(List<E> list, Object mutex) {
super(list, mutex);
}
SynchronizedList(List<E> list, Object mutex) {
super(list, mutex);
this.list = list;
}
Collections.synchronizedMap(new HashMap<K,V>)
//通过同步锁synchronized(mutext)声明同步代码块保证线程安全
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<>(m);
}
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;
}
}
由源码可知,SynchronizedList和SynchronizedMap都是借助同步代码块实现使之变得线程安全,所以这种方式性能问题并未得到很好的解决。
四 Map接口

HashMap
根据key的hashCode进行存储,由于可以直接计算出hashCode值,所以可以直接定位对应值的位置,所以反问速度是很快的,它允许一个key为null,当key为对象的时候,该对象需要重写hashCode()方法和equals()方法保证对象的唯一性,从而满足HashMap中key的唯一性,而HashMap的value是没有任何限制。
在JDK1.7和JDK1.8中的HashMap的默认初始容量为16,loadFactor均为0.75。
JDK1.7中HashMap扩容的源码
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//扩容
void resize(int newCapacity) {//传入新的容量
Entry[] oldTable = table;//引用扩容前的Entry数组
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {//扩容前的数组大小如果已经达到最大(2^30)了
threshold = Integer.MAX_VALUE;//修改阈值为int的最大值(2^31-1),这样以后就不会扩容了
return;
}
Entry[] newTable = new Entry[newCapacity];//初始化一个新的Entry数组
//table重新hash到新table上
transfer(newTable, initHashSeedAsNeeded(newCapacity));//将数据转移到新的Entry数组中
table = newTable;//HashMap的table属性引用新的Entry数组
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);//修改阈值
}
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
/**
* Creates new entry.
*/
//h=hash值,k=新元素key,v=新元素的value,n=原链表
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;//next指向了原链表,新元素插入到了原链表头部
key = k;
hash = h;
}
//扩容后转移数据
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) { //遍历旧的Entry数组
while(null != e) {
//造成死循环的关键代码(截图中的步骤一)
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity); //重新计算每个元素在数组中的下标位置
e.next = newTable[i]; //由于采用头插法,链表顺序颠倒(newTable[i]=next)
newTable[i] = e;
e = next;
}
}
}
JDK 1.7 HashMap的数据结构
在JDK1.7中HashMap的数据结构为数组 + 链表。
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
//通过key计算hash值
int hash = hash(key);
//通过hash值的计算数组下标
int i = indexFor(hash, table.length);
//遍历该下标位置的链表,如果有存在相同的key,就会进行替换
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
//没有相同的key,就需要插入元素,修改次数+1
modCount++;
//添加元素
addEntry(hash, key, value, i);
return null;
}
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
//如果size大于阈值后就会扩容成原来的两倍
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n; //next指向了原链表,新元素插入到原链表头部
key = k;
hash = h;
}
JDK1.7解决hash碰撞(hash冲突)问题
向HashMap中插入数据时,先根据key进行Hash运算【int h = hash(key)】获取key的hash值h,然后根据方法indexFor()将hash值h与数组的长度进行逻辑与运算【int i = indexFor(hash, table.length)】从而得到数组下标。
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 : "length must be a

本文详细介绍了Java集合框架中线程安全的相关知识,包括Vector、Collections.synchronizedList和synchronizedMap的线程安全实现,HashMap在JDK1.7和1.8的扩容及解决哈希冲突的策略,以及ConcurrentHashMap的并发控制和优化。此外,还提到了CopyOnWriteArrayList的特性及其适用场景,以及ConcurrentLinkedQueue作为高效并发队列的实现。文章深入剖析了各种集合类在多线程环境下的行为和性能,并探讨了数据结构与并发安全的平衡。
最低0.47元/天 解锁文章
714

被折叠的 条评论
为什么被折叠?



