如需转载,请根据 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 许可,附上本文作者及链接。
本文作者: 执笔成念
作者昵称: zbcn
本文链接: https://1363653611.github.io/zbcn.github.io/2019/12/09/concurrency_02-%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8/
concurrency-线程安全
I . 导言
A . 要编写线程安全的代码,其核心在于要对状态访问操作进行管理
- A.A shared 共享的
- 共享意味着变量可以由多个线程同时访问
A.B Mutiable 可变的
- 共享意味着变量可以由多个线程同时访问
- 意味着变量的值在其生命周期内可以发生变化
B . 控制对象的线程安全
- B.A 采用同步机制协同对象可变状态的访问
C . java 中的同步机制 - C.A synchronized 提供了独占加锁的方式
- C.B volatile 类型的变量
- C.C 显示 锁 Explicit lock
- C.D 原子变量
D . 当多个线程同时访问一个可变的状态的变量时,没有使用合适的同步,就会出现错误。
解决方案:
- D.A 不在线程之间共享该变量
- D.B 将该变量的状态设置为不可变
- D.C 访问状态变量时使用同步
II . 线程安全性
A 正确性
- A.A 某个类的行为与其规范完全一致
- A.B 单线程:所见即所指 we known when we see it
B 描述对象的操作结果 - B.A 不变性条件 Invariant
- B.B 后验条件 Postcondition
C 线程安全类 - C.A 当多个线程访问某个类时,这个类都能表现出正确的行为,那么这个类就是线程完全的
- C.B 在线程安全类中封装了必要的同步机制,因此客户端无需进一步采用同步措施
D 无状态对象 - D.A 即不包含任何域,也不包含对其他类的引用。计算过程中的临时状态仅存在于线程栈上的局部变量中,并且只能由正在执行的线程访问
- D.B 无状态对象一定时现线程安全的
III . 原子性
A . 竞态条件 Race Condition
- A.A 并发编程中,由于不恰当的执行时序而出现不正确的结果
- A.B 常见的竞态条件
- 先检查后执行(Check Then-Act)操作
- A.C 竞态条件并不是总会发生错误,还需要某种不恰当的执行时序
- A.D 避免竞态条件的方式
- 某个线程修改该变量时,通过某种方式防止其他线程使用这个变量。从而确保其他线程只能在修改该完成之前或者之后读取和修改状态,而不是在修改状态的过程中
B . 延时初始化中的竞态条件
- B.A 懒汉式单利模式 (不加线程安全控制)
C . 复合操作
- C.A value++ 复合操作:读取-修改-写入
D . 原子操作
- D.A 对于访问同一个状态所有的操作(包括该操作本身)来说,这个操作是一个以原子的方式进行的操作(不可分割)
- D.B java.lang.concurrent.atomic 包中包含了一些原子变量类,用于实现在数值和对象引用上的原子状态转换
IV . 加锁机制
A . 要保持状态一致性,就需要在单个原子操作中更新所有相关的状态变量
B . 内置锁
B.A 同步代码块 Synchronized Block
- 包含两部分
- 作为锁的对象引用
- 作为由这个锁保护的代码快
- 以sychronized 修饰方法,就是横跨整个方法体的同步代码快
- 静态的 sychronized 方法,以Class 对象作为锁
- 包含两部分
B.B 可见性
B.C 每个java对象都可以用作实现同步的锁,这个锁被称为 内置锁 (Intrinsic Lock) 或者监视器锁(Monitor Lock)
C . 重入
- C.A 内置锁是可重入的
- C.B 某个线程试图获得一个已经由它自己持有的锁,那么这个请求就会成功
- C.C “重入”意味着锁的粒度是线程
- C.D 重入的实现方式
V . 用锁来保护状态
A . 如果在复合操作的执行过程中,持有一个锁,那么会使复合操作变成原子操作
B . 对于可能被多个线程同时可以访问的可变状态变量,在访问它时都需要持有同一个锁,在这种情况下,我们称这个变量是由这个锁保护的
C . 每个对象都有一个内置锁
D . 每个共享的和可变的变量都应该用一个锁来保护,从而使得维护人员知道是哪一个锁
E . 常见的枷锁约定
- E.A 将所有可变的状态都封装在对象的内部,并通过对象的内置锁对所有访问可变状态代码路径进行同步,使得该对象上不会发生并发访问。
- E.B 对于每个包含多个变量的不变性条件,其中设计的所有变量都需要由同一个锁来保护
VI . 活跃性与性能
A . 合理的同步代码块(简单性和性能之间要进行权衡)
- A.A 简单性
- A.B 安全性(必须满足)
- A.C 性能
B . 在简单性和性能之间存在着相互制约,当实现某个策略时,一定不能盲目的为了性能而牺牲简单性
C . 当执行时间较长的计算或者可能无法快速完成的操作时(eg:网络i/o 或者控制台I/O)。一定不要持有锁,。