线性安全性
要编写线性安全的代码,核心在于要对状态访问操作进行管理,特别是共享的和可变的状态:
“状态”指存储变量中的数据
“共享”指能被多个线程同时访问
“可变”指可被修改
如果多个线程同时访问一个可变的变量,那么程序就会出现问题,有三种方法可以修复:
- 变量不共享
- 变量不可变
- 访问时使用同步
程序的封装性(私有域)越好,就越容易实现线程安全性
什么是线程安全性?
线程安全性是状态相关的,可能是应用于一个对象,也可能应用于一个程序。
在线性安全性的概念中,最核心的是正确性,什么是正确性?
良好的规范是定义一些不变性条件和后验条件来描述“是否正确”。但是一般情况下都不会去按照这个规范,而且通过“类的代码能正确的工作”来判断。单线程下这种“正确工作”可以表现为“所见即所知”,但多线程下时,是否能“正确的工作”往往无法直观的去判断。
定义:被多个线程访问,类能否正确的工作。
无状态的对象一定是线性安全的
原子性
一个不可分割的操作是一个原子操作
多线程下,由于原子性,会导致类无法正确的工作(尽管在单线程中看起来正常)
竞态条件:
当两个线程竞争同一资源时,由于不恰当的执行时序会出现错误的结果,常见的竞态条件如“先检查后执行”、“读取—修改—写入”,其本质是由于基于一个失效的状态进行后续的操作
复合操作?
复合操作是包含了一组必须以原子方式执行的操作以确保线程安全。
意味着:如果复合操作不易原子方式执行,则会有线程安全问题
如c++
在一些简单场合可以使用原子类 Atomic 系列来使用符合操作的原子性,从而实现线程安全,但是使用Atomic 并不能保证类的线性安全性,复杂的情形下还是需要使用锁来实现。
可重入锁以及实现:
执行锁的线程可以重复执行自身
互斥计数器:0表示无线程持有,占有后变更为1,重入后+1,跳出后-1
可重入解决什么问题?
解决子类同步代码调用本类或父类中的其他同步代码,否则会死锁
线程带来的问题:
- 安全性问题:程序出现错误结果,如竞态条件
- 活跃性问题:程序没出现正确结果,如死锁,饥饿
- 性能问题:正确的结果出现的很晚