【笔记】java并发编程实战 (3)

任务不会在其自己拥有的线程中执行,而是在某个服务(如线程池)拥有的线程中执行,这就是大多数可阻塞的库函数都只是抛出interruptedException作为中断响应,它们永远不会在某个由自己拥有的线程中运行,因此它们为任务或库代码实现了最合理的取消策略:尽快退出执行流程,并把中断信息传递给调用者,从而使调用栈中的上层代码可以采取进一步的操作

当取消一个生产者-消费者操作时,需要同时取消生产者和消费者

关闭钩子是指通过Runtime.addShutdownHook注册的但尚未开始的线程

线程可分为两种:普通线程和守护线程。在JVM启动时创建的所有线程中,除了主线程以外,其他的线程都是守护线程(例如垃圾回收器以及其他执行辅助工作的线程)。当创建一个新线程时,新线程将继承它的线程的守护状态,因此在默认情况下,主线程创建的所有线程都是普通线程。普通线程与守护线程之间的差异仅在于当线程退出时发生的操作

死锁:过度加锁可能导致”锁顺序死锁”,使用线程池和信号量来限制对资源的使用,可能会导致”资源死锁”

在并发程序中,对可伸缩性的最主要威胁就是独占方式的资源锁

有两个因素将影响在锁上发生竞争的可能性:锁的请求频率,以及每次持有该锁的时间

Amdahl定律告诉我们,程序的可伸缩性取决于在所有代码中必须被串行执行的代码比例

提升可伸缩性可通过:减少锁的持有时间,降低锁的粒度,以及采用非独占的锁或非阻塞锁来代替独占锁

当某个类第一次被加载时,JVM会通过解释字节码的方式来执行它。在某个时刻,如果一个方法运行的次数足够多,那么动态编译器会将它编译为机器代码,当编译完成后,代码的执行方式将从解释执行变为直接执行

当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。在这些情况下,”插队”带来的吞吐量提升则可能不会出现

在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的、可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁。否则,还是应该优先使用synchronized

读/写锁:一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行

可以使用java语言和类库提供的底层机制来构造自己的同步机制,包括内置的条件队列、显示的Condition对象以及AbstractQueuedSynchronized框架

当使用条件等待时(Object.wait或Condition.await):a)通产都有一个条件谓词-包括一些对象状态的测试,线程在执行前必须首先通过这些测试b)在调用wait之前测试条件谓词,并且从wait中返回时再次进行测试c)在一个循环中调用wait d)确保使用与条件队列相关的锁来保护构成条件谓词的各个状态变量e)当调用wait、notify或notifyAll等方法时,一定要持有与条件队列相关的锁f)在检查条件谓词之后以及开始执行相应的操作之前,不要释放锁

活跃性故障:死锁、活锁、丢失的信号。丢失的信号:线程必须等待一个已经为真的条件,但在开始等待之前没有检查条件谓词

大多数情况下应该有限选择notifyAll而不是单个的notify。只有同时满足两个条件时,才能用单一的notify而不是notifyAll:a)所有等待线程的类型都相同,只有一个条件谓词与条件队列相关,并且每个线程在从wait返回后将执行相同的操作b)单进单出,在条件变量上的每次通知,最多只能唤醒一个线程来执行

对于每个依赖状态的操作,以及每个修改其他操作依赖状态的操作,都应该定义一个入口协议和出口协议,入口协议就是该操作的条件谓词,出口协议则包括:检查被该操作修改的所有状态变量,并确认它们是否使用某个其他的条件谓词变为真,如果是,则通知相关的条件队列

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgjgps.html