From 5ccb0f300da42ed4c410f14496b1f5fe01c4f359 Mon Sep 17 00:00:00 2001 From: Omooo <869759698@qq.com> Date: Mon, 15 Jun 2020 07:38:24 +0800 Subject: [PATCH] =?UTF-8?q?Update=20=E9=9B=86=E5=90=88=E6=BA=90=E7=A0=81.m?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blogs/Java/口水话/集合源码.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/blogs/Java/口水话/集合源码.md b/blogs/Java/口水话/集合源码.md index 4a21df8..a499ca9 100644 --- a/blogs/Java/口水话/集合源码.md +++ b/blogs/Java/口水话/集合源码.md @@ -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 节点删掉然后在插入,否则就只能扩容了。