|
|
|
@ -16,8 +16,10 @@ |
|
|
|
|
10. TreeSet |
|
|
|
|
11. CopyOnWriteArrayList |
|
|
|
|
12. ConcurrentHashMap |
|
|
|
|
13. SparseArray |
|
|
|
|
14. ArrayMap |
|
|
|
|
13. LinkedBlockingQueue |
|
|
|
|
14. SynchronousQueue |
|
|
|
|
15. SparseArray |
|
|
|
|
16. ArrayMap |
|
|
|
|
|
|
|
|
|
#### ArrayList |
|
|
|
|
|
|
|
|
@ -128,6 +130,24 @@ ConcurrentHashMap 在 put 时,在一个 for 死循环里面,也就是一定 |
|
|
|
|
|
|
|
|
|
ConcurrentHashMap 的 get 和 HashMap 基本无差。 |
|
|
|
|
|
|
|
|
|
#### LinkedBlockingQueue |
|
|
|
|
|
|
|
|
|
LinkedBlockingQueue 即链表阻塞队列,它实现了 BlockingQueue 接口,BlockingQueue 在 Queue 的基础上加上了阻塞的概念;链表维护了先入先出的队列,新元素被放在队尾,获取元素从队头拿;当我们调用 put 时,队列满了就一直阻塞,也可以调用 offer 阻塞一段时间返回 false,调用 take 时,如果队列为空也是一直阻塞,也可以调用 poll 阻塞一段时间返回 null。 |
|
|
|
|
|
|
|
|
|
LinkedBlockingQueue 内部构成简单来说,可以分为三个部分:链表存储 + 锁 + 迭代器。它内部有两把 ReentrantLock 锁,分别是 put 锁和 take 锁,保证了队列操作的线程安全,设计两把锁,是为了 put 和 take 两种操作可以同时进行,互不影响。 |
|
|
|
|
|
|
|
|
|
在 put 时,先调用 ReentrantLock 的 lockInterruptibly 设置可中断锁,然后判断队列是否满了,如果满了就调用 Condition 的 await 函数无限等待,否则就添加到队列尾部;如果这时队列不为空,就唤醒一个 take 的等待线程,最后在解锁。 |
|
|
|
|
|
|
|
|
|
在 take 时,也是先加 take 锁,然后判断队列是否为空,为空就无限等待,否则就删除链表头节点返回,如果这时队列没有满,就唤醒一个 put 的等待线程,最后在解锁。 |
|
|
|
|
|
|
|
|
|
LinkedBlockingQueue 主要用在线程池中,当我们使用 Executors 的 newFixedThreadPool 或 newSingleThreadPool 创建线程池时,阻塞队列用的就是 LinkedBlockingQueue,但是通常我们不建议这样直接使用 Executors 创建线程池,因为 LinkedBlockingQueue 的默认队列大小是 Integer.MAX_VALUE,可能会爆内存。 |
|
|
|
|
|
|
|
|
|
在 Android 中,AsyncTask 在核心线程池不够用的情况下触发任务拒绝策略时,会用到 LinkedBlockingQueue。AsyncTask 它使用是一个单线程池, |
|
|
|
|
|
|
|
|
|
#### SynchronousQueue |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### SparseArray |
|
|
|
|
|
|
|
|
|
SparseArray 是 Android 中一种特有的数据结构,用来替代 HashMap 的。初始化时容量为 10,它里面有两个数组,一个是 int[] 数组存放 key,一个是 Object[] value 数组。也就是它的 key 只能为 int;在 put 时,会根据传入的 key 进行二分查找找到合适的插入位置,如果当前位置有值或者是 DELETED 节点,就直接覆盖,否则就需要拷贝数组后移一位,空出一个位置让其插入。如果数组满了但是还有 DELETED 节点,就需要调用 gc 方法,gc 方法所做的就是把 DELETED 节点后面的数前移,也就是真正的把 DELETED 节点删掉然后在插入,否则就只能扩容了。 |
|
|
|
|