1. JDK 源码学习方法
1. 演绎推导法
- 示例:因果推理。
- 因为 JAVA 中只提供了 BIO 和 NIO 两种方式,所以一切框架中,涉及到网络处理的,都可以用这两个知识点去探究原理。
2. 归纳总结法
- 示例:可能正确的猜想。
- 线上 10 台服务器,有 3 台总是每天自动会重启,收集相关信息后,发现是运维在修改监控系统配置的时候,漏掉了提高这 3 台机器的重启阀值。
3. 类比法
- 集群概念就好像是马在拉车,一匹马拉不动的时候,就使用多匹马去拉。
- 分布式的概念,就像是理发的过程中,洗头发和剪头发是不同的人负责的。
2. 推理 HashMap 的实现
1. 涉及内容
-
数据要存储
- 涉及到数据结构:数组、链表、栈、树、队列。
-
数组的插入和查找
- 顺序查找:插入时按先后顺序插入,查找时轮询扫描进行对比。
- 二分查找:插入时进行排序;查找时将 n 个元素分成大致相等的两部分,减少复杂度。
- 分块查找:分块查找是二分查找和顺序查找的一种改进。
- 哈希表:对元素的关键信息进行 hash 计算,求出下标后直接插入或查找。常用的实现是除留余数法。
-
哈希冲突,数组位置已存在值。
- hash(key2)=hash(key1)。链地址法;ReHash1(key2) 再次计算 hash;
-
合理控制数组和链表的长度。
- 动态扩容 resize()。
2. HashMap(JDK1.7)
3. HashMap(JDK1.8)
- JDK1.8 后,HashMap 中链表中元素超过一个数量后,转变为红黑树结构。
3. ConcurrentHashMap
1. JDK1.7
2. JDK1.8
4. ConcurrentSkipListMap
4. List
1. CopyOnWriteArrayList 优缺点
- CopyOnWriteArrayList 容器即写时复制的容器。
- 优点:
- 和 ArrayList 比较,优点是并发安全,
- 缺点:
- 多了内存占用:写数据是 copy 一份完整的数据,单独进行操作。占用双份内存。
- 数据一致性:数据写完之后,其他线程不一定马上能读取到最新内容。
2. CopyOnWriteArrayList 原理分析
5. Set
实现 | 原理 | 特点 |
---|---|---|
HashSet | 基于 HashMap 实现 | 非线程安全 |
CopyOnWriteArraySet | 基于 CopyOnWriteArrayList | 线程安全 |
ConcurrentSkipListSet | 基于 ConcurrentSkipListMap 实现 | 线程安全,有序,查询快 |
6. Queue
1. Queue API
- Queue - 队列数据结构的实现。分为阻塞队列和非阻塞队列。下列蓝色区块,为阻塞队列特有方法。
2. 常用队列
- ArrayBlockingQueue
- LinkedBlockingQueue
- ConcurrentLinkedQueue (并发行更好,cas 机制)
- SynchronousQueue
- PriorityBlockingQueue
3. SynchronousQueue(同步队列)
-
take会阻塞,直到取到元素
public static void test01() throws InterruptedException { new Thread(() -> { try { Thread.sleep(3000); syncQueue.put("put_element"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); System.out.println("begin to take..."); System.out.println(syncQueue.take()); System.out.println("take done..."); }
-
put 时被阻塞,直到被 get
public static void test02() throws InterruptedException { new Thread(() -> { try { Thread.sleep(3000); System.out.println(syncQueue.poll()); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); System.out.println("begin to put..."); syncQueue.put("put_element"); System.out.println("put done..."); }
-
若没有 take 方法阻塞等待,offer 的元素可能会丢失
public static void test03() { syncQueue.offer("offered_element"); System.out.println(syncQueue.poll()); }
-
poll 取不到元素,就返回 null,如果正好有 put 被阻塞,可以取到
public static void test04() throws InterruptedException { new Thread(() -> { try { syncQueue.put("put_element"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); Thread.sleep(1000); Object obj = syncQueue.poll(); System.out.println(obj); }
-
peek 永远只能取到 null,不能让 take 结束阻塞
public static void test05() throws InterruptedException { new Thread(() -> { try { syncQueue.put("put_element"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); Thread.sleep(200L); Object obj = syncQueue.peek(); System.out.println(obj); }
4. PriorityBlockingQueue(优先级队列)
-
简单使用
public static void main(String[] args) { PriorityBlockingQueue<String> queue = new PriorityBlockingQueue<>(); queue.put("48"); queue.put("01"); queue.put("12"); queue.put("27"); queue.put("31"); for (;queue.size() > 0;) { try { System.out.println(queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
自定义比较规则
public class Demo4_PriorityBlockingQueue { static PriorityBlockingQueue<Student> queue = new PriorityBlockingQueue<>(5, // (stu1, stu2) -> stu1.age - stu2.age Comparator.comparingInt(stu -> stu.age) ); public static void main(String[] args) throws InterruptedException { queue.put(new Student(10, "Emily")); queue.put(new Student(20, "Tony")); queue.put(new Student(5, "Baby")); for (; queue.size() > 0;) { System.out.println(queue.take().name); } } } class Student { public int age; public String name; public Student(int age, String name) { this.age = age; this.name = name; } }