第六章 Java8与并发

20 篇文章 0 订阅

第六章 Java8与并发

6.1 Java8的函数式编程简介

6.1.1 函数作为一等公民

  • 将函数作为参数传递给另外一个函数;函数作为另一个函数的返回值。

6.1.2 无副作用

  • 副作用,指的是除了返回值之外,还修改了函数外部的状态。无副作用实际上很难做到。

6.1.3 申明式的(Declarative)

  • 函数式编程是申明式的编程方式。相对于命令式而已,命令式程序设计喜欢大量使用可变对象和指令。
  • EX:参考253

6.1.4 不变的对象

6.1.5 易于并行

6.1.6 更少的代码

6.2 函数式编程基础

6.2.1 FunctionalInterface 注解

  • 函数式接口的概念:只定义了单一抽象方法的接口。接口默认方法和Object实现的方法都不算抽象方法,因此存在多于一个方法的接口也可以为函数式接口。
  • 如果一个函数满足函数式接口的定义,即使不使用@FunctionalInterface,编译器依然会把它看作函数式接口。

6.2.2 接口默认方法

6.2.3 lambda表达式

  • 即匿名函数。一段没有函数名的函数体

6.2.4 方法引用

  • 静态方法引用:ClassName::methodName
  • 实例上的实例方法引用:instanceReference::methodName
  • 超类上的实例方法引用:super::methodName
  • 类型上的实例方法引用:ClassName::methodName
  • 构造方法引用:Class::new
  • 数组构造方法引用:TypeName[]::new

6.3 一步一步走入函数式编程

  • -Djdk.internal.lambda.dumpProxyClass启动lambda表达式中间类型输出。

6.4 并行流与并行排序

6.4.1 使用并行流过滤数据

6.4.2 从集合得到并行流

6.4.3 并行排序

6.5 增强的Future:CompletableFuture

  • 实现了Future接口和CompletionStage接口,其为函数式编程的流式调用准备的。

6.5.1 完成了就通知我

  • 通过CompletableFuture可以手动设置CompletableFuture的完成状态

6.5.2 异步执行任务

  • 注意:java8新增ForkJoinPool.commonPool()方法。它可以获得一个公共的ForkJoin线程池。这个公共线程池中的所有线程都是Daemon线程。这意味着如果主线程退出,这些线程无论是否执行完毕,都会退出系统。

6.5.3 流式调用

6.5.4 CompletableFuture中的异常处理

  • exceptionally

6.5.5 组合多个CompletableFuture

6.6 读写锁的改进:StampedLock

  • Java8引入的新的一种锁机制。可以认为它是读写锁的一个改进版本。读写锁式悲观策略,读锁会完全阻塞写锁,大量读线程会导致写线程饥饿;StampedLock是一种乐观的策略,这种乐观的锁策略非常类似无锁的操作,使得乐观锁完全不会阻塞写线程。

6.6.1 StampedLock使用示例

EX:参考275

6.6.2 StampedLock 的小陷阱

  • StampedLock内部实现时,使用类似于CAS操作的死循环反复尝试的策略。在它挂起线程时,使用的是Unsafe.park函数,在遇到线程中断时,会直接返回。而在StampedLock的死循环逻辑中,没有处理有关中断的逻辑。因此,这就会导致阻塞在park上的线程被中断后,会再次进入循环。而当退出条件得不到满足时,就会发生疯狂占用CPU的情况。
  • EX:参考276

6.6.3 有关StampedLock的实现思想

  • 内部实现是基于CLH锁的。CLH锁是一种自旋锁,它保证没有饥饿发生,并且可以保证FIFO的服务顺序。
  • CLH基本思想如下:
    • 锁维护一个等待线程队列,所有申请锁,但是没有成功的线程都记录在这个队列中。每一个节点(代表一个线程),保存一个标记位(locked),用于判断当前线程是否已经释放锁。只要前序节点没有释放锁,则表示当前线程还不能继续执行,因此会自旋等待。

6.7 原子类的增强

6.7.1 更快的原子类:LongAdder

  • AtomicInteger存在的问题:高并发竞争激烈时,修改失败的几率会比较高,大量失败修改导致多次循环尝试,因此性能会受到影响。
  • 方案:减小锁粒度,分离热点。一种可行的方案就是伪造ConcurrentHashMap,将热点数据分离。如:将AtomicInteger的内部核心数据value分离成一个数组,每个线程访问时,通过哈希等算法映射到其中一个数字进行计数,而最终的计数结果,则为这个数组的求和累加。热点数据value被分离成多个单元cell,每个cell独自维护内部的值。
  • LongAdder的另一种优化手段是避免了伪共享。但并不是padding这种看起来比较碍眼的做法,而是引入了新的注解,@sun.misc.Contended,对Cell进行注解。开启该注解的参数-XX:-ResrictContended,否则,该注解会被忽略。

6.7.2 LongAdder 的功能增强版:LongAccumulator

  • 和LongAdder有着共同的父类Striped64.因此其优化同LongAdder一样,将一个long型整数进行分割,存储在不同的变量中,以防止多线程竞争。其只是对LongAdder的扩展,对于LongAdder来说,只是每次对给定的整数执行一次加法,而LongAccumulator则可以实现任意函数操作。
  • 构造函数参数:accumulatorFuction为需要执行的二元函数(接收两个long型参数并返回long),第二个参数是初始值。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值