final HashMap<String, String> map = new HashMap<>(2);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
map.put(UUID.randomUUID().toString(), "");
}
}, "ftf" + i).start();
}
}
}, "ftf");
t.start();
t.join();
效率低下的HashTable:HashTable容器使用synchronized来保证线程安全
ConcurrentHashMap使用锁分段技术:
由Segment数组结构和HashEntry数组结构组成
Segment是一种可重入锁(ReentrantLock)
HashEntry用于存储键值对数据
Segment的结构和HashMap类似,是一种数组和链表结构
一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素
当对HashEntry数组的数据进行修改时,须先获得与它对应的Segment锁

public V get(Object key) {
// 1.先经过一次再散列(一次再散列)
int hash = hash(key.hashCode());
// 2.使用这个散列值通过散列运算定位到Segment,再通过散列算法定位到元素(两次散列定位)
return segmentFor(hash).get(key, hash);
}
put操作:
因为需要对共享变量进行写入操作,所以在操作共享变量时必须加锁
插入操作需要经历两个步骤:
是否需要扩容:在插入元素前会先判断Segment里的HashEntry数组是否超过容量,如果超过阈值,则对数组进行扩容(HashMap是先插入再判断扩容,很可能扩容后就没有元素要插入了)
如何扩容:创建一个容量是原来容量两倍的数组,然后将原数组里的元素进行再散列后插入到新的数组里(只对某个segment进行扩容)
size操作:先尝试2次通过不锁住Segment的方式来统计各个Segment大小,如
果统计的过程中,容器的count发生了变化,则再采用加锁的方式来统计所有Segment的大小
支持两个附加操作的队列:
常用于生产者和消费者的场景,即生产者用来存放元素、消费者用来获取元素的容器
ArrayBlockingQueue:
ReentrantLock(fair))LinkedBlockingQueue:
PriorityBlockingQueue:
DelayQueue:
支持延时获取元素的无界阻塞队列
在创建元素时可以指定多久才能从队列中获取当前元素(只有在延迟期满时才能从队列中提取元素)
SynchronousQueue:
LinkedTransferQueue:
LinkedBlockingDeque:
由链表结构组成的双向阻塞队列
在多线程同时入队时减少了一半的竞争
指某个线程从其他队列里窃取任务来执行(因为子任务会被分到不同的队列,并且由不同线程负责,而不同线程执行速度不同)
为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列(被窃取任务线程从双端队列的头部拿任务,窃取任务的线程从双端队列的尾部拿任务)
优点:
缺点:
分割任务:fork类来把大任务分割成子任务
执行任务并合并结果:
@Override
protected Integer compute() {
int sum = 0;
// 如果任务足够小就计算任务
boolean canCompute = (end - start) <= THRESHOLD;
if (canCompute) {
// 任务的具体逻辑
for (int i = start; i <= end; i++) {
sum += i;
}
} else {
// 如果任务大于阈值,就分裂成两个子任务计算
int middle = (start + end) / 2;
CountTask leftTask = new CountTask(start, middle);
CountTask rightTask = new CountTask(middle + 1, end);
// 执行子任务
leftTask.fork();
rightTask.fork();
// 等待子任务执行完,并得到其结果
int leftResult=leftTask.join();
int rightResult=rightTask.join();
// 合并子任务
sum = leftResult + rightResult;
}
return sum;
}