并发编程常见方法(三)

是的,这个话题还没有说完。如果能用三言两语概括,它就不是并发编程了。

接下来是一些难以概括的知识点,但仍然很有用。


CompletableFuture

Java 8 新加入的特性,比起Future,它支持链式调用和回调函数,让复杂的异步操作变得容易。然而,回调函数的底层编排比较复杂(它属于Java标准库最难看懂,也是最”complicated”的一些),但调用者一般无需考虑它们的实现。

它的声明方式比较独立。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello");

future.get()可以等待结果。这和普通的Future一样会阻塞。

然而,它的特点在于灵活的回调函数。(可能会想到JavaScript的Promise,但CompletableFuture背后并没有事件循环)可以像这样优雅地链式调用:

CompletableFuture.supplyAsync(() -> 1)
	.thenApply(i -> i + 1)
	.thenApply(i -> 1 * 2);

这里思路就打开了。

常用方法

CompletableFuture.runAsync()创建的任务没有返回值,一般不做链式调用。CompletableFuture.supplyAsync()有返回值。


thenApply()传入一个函数(Function),在运行到此处时执行函数。

thenAccept()传入一个Customer,它通常放在调用链的最后,因为执行后会返回CompletableFuture<Void>

thenRun()只是运行函数,此函数不会得到参数,但会在特定时机运行。

exceptionally()接受一个函数,它有一个参数,是异常的Exception类。它会捕获它之前整个调用链中出现的异常,除非它们在自己的函数里已经被处理。


CompletableFuture.allOf(CompletableFuture a, b, c...)可以组合几个任务,它们是并发的。只有这些任务全部完成,它才会执行下一步。

CompletableFuture.anyOf(a, b, c)与前者类似,但任意任务完成就会执行下一步。


有趣的事实(也许你已经知道了):CompletableFuture是一个 Monad 。

ReadWriteLock

一个著名的规律是,有多个线程同时读取某个资源,或者单个线程写入某个资源时,是线程安全的;但一读一写或者有多个写入者时,就可能出现问题。Java的ReadWriteLock就是为了解决这种问题的。

读取者和写入者加锁的方式不一样,分别是lock.readLock().lock()lock.writeLock().lock()。它们可以分别unlock

与一般的锁不同,它允许多个读取者同时存在,在读多写少的情景下能大幅提升效率。

Condition

类似于Object的wait/notify方法,Condition用于表示一个状态,即某个资源是否已存在。它们很适合生产者/消费者模型。

Condition一般派生自Lock,它们总是和某个锁绑定。声明方式类似Condition c = lock.newCondition()

Condition拥有await()signal()signalAll()等方法,和经典的P/V操作如出一辙。


评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注