Java并发编程实战
Java Concurrency in Practice
Chapter 1 简介
多程序产生原因:提高资源利用率,公平性,便利性
线程是轻量级进程
线程优势:多处理器,建模简单,异步事件响应,用户界面反应灵敏
线程风险:安全性,活跃性,性能问题
Chapter 2 线程安全性
正确使用线程和锁
线程安全代码的核心是对状态访问操作进行管理,特别是对共享的(Shared)和可变的(Mutable)状态的访问
线程安全最核心的是正确性。
原子性
竞态条件 因为不恰当的执行条件而出现不正确的结果
复合操作
加锁机制
内置锁(Intrinsic Lock)或监视器锁(Monitor Lock) 相当于一种互斥锁。线程进入同步代码会自动获得锁,离开会自动释放锁。
重入 这意味着获取锁的操作粒度是“线程”而不是“调用”。Java中同一线程可以重入自己持有的内置锁,不然可能会死锁,如递归调用的时候。pthread互斥体的粒度是“调用”。
用锁保护状态
只有在写入共享变量时才需要使用同步是错误的。
每个共享的和可变的变量都应该只由一个锁来保护。
包含多个变量的不变性条件,涉及的所有变量都需要由同一个锁来保护。
活跃性和性能
简单性和性能之间存在制约,但是不能盲目为了性能而牺牲简单性,因为可能会破坏安全性。
执行长时间的计算或者操作的时候,不要持有锁。
Chapter 3 对象的共享
关键字synchronized 一方面可以用于实现原子性或者确定“临界区(Critical Section)”,另一个重要的方面在于内存可见性(Memory Visibility)。
安全发布
可见性
为了确保多个线程之间对内存的写入操作的可见性,必须使用同步。
失效数据
非原子的64位操作
最低安全性(out-of-thin-airsafety)适用于绝大多数变量
Java内存模型要求,变量的读写操作都必须是原子操作,但对于非volatile类型的long和double变量,JVM允许将64位的读操作或者写操作分解为两个32位操作。
加锁的含义不仅仅局限于互斥行为,还包括内存可见
Volatile变量
是一种弱同步机制
读取volatile变量时总会返回最新写入的值。
访问volatile变量不会执行加锁操作
加锁机制既可以确保可见性,又可以确保原子性,而volatile变量只能确保可见性。
发布与逸出
“发布(Publish)”一个对象的意思是指,使对象能够在当前作用域之外的代码中使用。
当某个不应该发布的对象被发布时,这种情况就被称为逸出(Escape)。
不要在构造过程中使用this引用逸出。
线程封闭
仅在单线程里访问数据,就不需要同步
Ad-hoc线程封闭
维护线程封闭性职责完全由程序实现来承担。
很脆弱,尽量少用
栈封闭
栈封闭是线程封闭的一种特例,在栈封闭中,只能通过局部变量才能访问对象。(编者按:栈封闭简单说就是放在函数里且不会发布的局部变量)
ThreadLocal类
提供了get和set等访问接口的方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get总能返回由当前线程在调用set时设置的最新值。
ThreadLocal变量类似于全局变量,它能降低代码的可重用性,并在类之间引入隐含的耦合性,因此在使用时要格外小心。
不变性
不可变对象一定是线程安全的。
不可变对象满足以下条件:
对象创建后不可修改;
对象的所有域都是final类型;
对象是正确创建的(对象创建期间,this引用没有逸出)。
Final域 final类型的域是不能修改的,但是如果final域所引用的对象是可变的,这些被引用的对象是可以修改的。
安全发布
不正确的发布:正确的对象被破坏
没有同步可见性,会导致多线程读时产生无效值
不可变对象与初始化安全性
任何线程都可以在不需要额外同步的情况下安全访问不可变对象,即使在发布这些对象时没有使用同步。
安全发布的常用模式
安全的发布对象,对象的引用以及对象的状态必须同时对其他线程可见。
在静态初始化函数中初始化一个对象引用。
将对象的引用保存到volatile类型的域或者AtomicReferance对象中
将对象的引用保存到某个正确构造对象的final类型域中
将对象的引用保存到一个由锁保护的域中。
事实不可变对象(Effectively Immutable Object)
在没有额外同步的情况下,任何线程都可以安全地使用被安全发布的事实不可变对象。
可变对象 要安全地共享可变对象,这些对象就必须被安全地发布,并且必须是线程安全的或者由某个锁保护起来。
安全地共享对象 当发布一个对象时必须明确地说明对象的访问方式。
并发程序中使用和共享对象时,可以使用的实用策略
线程封闭
只读共享
线程安全共享
保护对象
对象的发布需求取决于它的可变性
不可变对象可以通过任意机制来发布。
事实不可变对象必须通过安全方式来发布。
可变对象必须通过安全方式来发布,并且必须是线程安全的或者由某个锁保护起来。
Chapter 4 对象的组合
Chapter 7 取消与关闭
Java没有提供任何机制来安全地终止线程。但它提供了中断(Interruption),这是一种协作机制,能够使一个线程终止另一个线程的当前工作。
7.1 任务取消
如果外部代码能在某个操作正常完成前将其置入“完成”状态,那这个操作就可称为可取消的(Cancellable)。
Java中没用一种安全的抢占式方法来停止线程,因此也没有安全的抢占方式来停止任务。
Last updated