跳至主要內容
JDK8的Stream

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。

特性及优点

  • 无存储。Stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
  • 为函数式编程而生。对Stream的任何修改都不会修改背后的数据源,比如对Stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新Stream。
  • 惰式执行。Stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
  • 可消费性。Stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。

Znyoung大约 4 分钟Java线程HashMap
ConcurrentHashMap

ConcurrentHashMap是如何保证线程安全的?

在JDK 1.7中,ConcurrentHashMap使用了分段锁技术,即将哈希表分成多个段,每个段拥有一个独立的锁。这样可以在多个线程同时访问哈希表时,只需要锁住需要操作的那个段,而不是整个哈希表,从而提高了并发性能。

虽然JDK 1.7的这种方式可以减少锁竞争,但是在高并发场景下,仍然会出现锁竞争,从而导致性能下降。

在JDK 1.8中,ConcurrentHashMap的实现方式进行了改进,使用分段锁和“CAS+Synchronized”的机制来保证线程安全。 在JDK 1.8中,ConcurrentHashMap会在添加或删除元素时,首先使用CAS操作来尝试修改元素,如果CAS操作失败,则使用Synchronized锁住当前槽,再次尝试put或者delete。这样可以避免分段锁机制下的锁粒度太大,以及在高并发场景下,由于线程数量过多导致的锁竞争问题,提高了并发性能。


Znyoung大约 8 分钟Java集合线程
CopyOnWrite是什么?如何保证线程安全?

Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。

从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。

CopyOnWriteArrayList相当于线程安全的ArrayList,CopyOnWriteArrayList使用了一种叫写时复制的方法,当有新元素add到CopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。


Znyoung大约 2 分钟Java线程
HashMap用在并发场景的问题

扩容过程

HashMap在扩容的时候,会将元素插入链表头部,即头插法。如下图,原来是A->B->C,扩容后会变成C->B->A”。

之所以选择使用头插法,是因为JDK的开发者认为,后插入的数据被使用到的概率更高,更容易成为热点数据,而通过头插法把它们放在队列头部,就可以使查询效率更高。

并发现象

  • JDK7循环引用(死循环)

假设有两个线程A和B,它们都在进行rehash操作。线程A处理到第i个元素时被挂起,线程B继续处理,处理完后线程A继续执行,此时线程A看到的链表顺序和原始链表顺序不一样,这就可能会形成环形链表。

如果在JDK7中,有两个线程同时对HashMap进行put操作,并且这两个put操作导致HashMap需要进行rehash,那么可能会出现一个线程看到的链表顺序和另一个线程看到的链表顺序不一致的问题,从而导致循环引用的产生。

在JDK8中,变成了尾插法,就没有循环引用的问题。


Znyoung大约 2 分钟Java线程HashMap