java 容器
数组
数组的扩容问题
ArrayList 的默认初始化容量为0,首次添加元素时,创建容量为(10 || 添加集合大小) ,以后每次扩容的话,为当前容量的1.5倍
public ArrayList() {
/*
初始化容量大小为0
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
*/
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//容量取 10 或 minCapacity
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容机制,向右移动1位 即1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
并发访问数组问题
当我们遍历的同时修改数组元素的时候我们会面临并发修改数组的问题,比如我们使用 iterator 迭代器进行数组的遍历,同时使用 arrayList.remove进行元素的删除,则会导致并发修改异常。
导致并发修改的问题相当于文件快照版本号的对比。
private class Itr implements Iterator<E> {
//在创建迭代器的时候会记录快照的版本号
int expectedModCount = modCount;
Itr() {}
}
//每次在next 或者 remove 的时候会进行快照的检查如果快照版本已经被修改则会导致 并发修改异常
public E next() {
//检查版本号是否匹配
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
解决方案:
使用 iterator 提供的 remove 方法,或使用 for() 从尾部进行迭代并删除(避免头部操作的话导致的数组越界异常)
HashMap
ConcurrentHashMap
CopyOnWriteArrayList
从两个方面入手分析,分别从 add 方法,代表 操作数组的所有写的方法,二是从iterator方法,代表所有读的方法
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();
}
}
public Iterator<E> iterator() {
//创建iterator 并将数组的引用传递给iterator ,由于没有加锁,现在我们知道,我们读取的数据,并不一定是实时可见的
return new COWIterator<E>(getArray(), 0);
}
Queue
queue 的继承关系图
queue 接口提供的方法
Throws exception | Returns special value | |
Insert | Queue#add add(e) | Queue#offer offer(e) |
Remove | Queue#remove remove() | Queue#poll poll() |
Examine 查看但不进行元素删除的操作 | Queue#element element() | Queue#peek peek() |
Deque 接口提供的方法
First Element (Head) | Last Element (Tail) | |||
Throws exception | Special value | Throws exception | Special value | |
Insert | Deque#addFirst addFirst(e) | Deque#offerFirst offerFirst(e) | Deque#addLast addLast(e) | Deque#offerLast offerLast(e) |
Remove | Deque#removeFirst removeFirst() | Deque#pollFirst pollFirst() | Deque#removeLast removeLast() | Deque#pollLast pollLast() |
Examine | Deque#getFirst getFirst() | Deque#peekFirst peekFirst() | Deque#getLast getLast() | Deque#peekLast peekLast() |
BlockingQueue接口提供的方法
Throws exception | Special value | Blocks | Times out | |
Insert | #add add(e) | #offer offer(e) | #put put(e) | #offer(Object, long, TimeUnit) offer(e, time, unit) |
Remove | #remove remove() | #poll poll() | #take take() | #poll(long, TimeUnit) poll(time, unit) |
Examine | #element element() | #peek peek() | not applicable | not applicable |
其他综合说明
ConcurrentLinkedQueue 是无限容量,LinkedBlockingQueue可以指定容量,在不指定容量时,默认容量为Integer 的最大值, ArrayBlockingQueue 需要指定容量,SynchronousQueue 容量为1,进行同步交换。
关于线程池还有一点说明,线程池中将command 从 blockingQueue 中取出时,并非用的 put 方法,而是使用的offer() 非阻塞方法。
//固定大小线程池使用的是 容量为interger 的最大值的 queue
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
//单线程池使用的是 容量为interger 的最大值的 queue
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
// 缓存线程池使用的是 同步的队列
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//使用的是DelayedWorkQueue
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
学习资料推广
我已经将springamqp 源码解析录制为视频上传到bibi,分为六个章节详细介绍了各个模块的具体内容
https://www.bilibili.com/video/BV1hN411Z7fn?share_source=copy_web
感兴趣的小伙伴可以看看
学习一下
录制不易,记得三联哦!