对象的状态是指存储在状态变量中的数据。对象的状态可能包括其他依赖对象的域。例如HashMap的状态不仅存储在HashMap本身,还存储在许多Map.Entry对象中。对象的状态中包含了任何可能影响其外部可见性为的数据。
共享意味着变量可以由多个线程同时访问,可变意味着变量的值在其生命周期内可能发生变化。
线程安全性:当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么这个类就是线程安全的。
当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这写线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么这个类就是线程安全的。
无状态对象一定是线程安全的。
无状态对象:不包含任何域,也不包含对其他类中域的引用。
当在无状态的类中添加一个状态时,如果该状态完全由线程安全的对象来管理,那么这个类仍然是线程安全的。
竞态条件
竞态条件:从多线程通信角度来讲,是指两个或多个线程对共享数据的操作时,最终的结果取决于这些线程的执行顺序。
- 最常见的竞态条件类型就是:先检查后执行(Check-Then-Act)。其本质就是-----基于一种可能失效的观察结果来做出判断或执行某个计算。
- 读取-修改-写入。基于对象之前的状态来定义对象状态的转换。
竞态条件并不是总是会产生,还需要某种不恰当的执行顺序。
要避免竞态条件问题,就必须在某个线程修改该变量时,通过某种方式防止其他线程使用这个变量,从而保证其他线程只能在修改操作完成之前或之后读取和修改状态,而不是修改过程中。
当在不变形条件中涉及多个变量时,各种变量之间并不是彼此独立的,而是某个变量的值会对其他变量的值产生约束。因此,当更新一个变量时,需要在同一个原子操作中对其他变量同时进行更新。要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量。
对于每个包含多个变量的不变性条件,其中涉及的所有变量都需要同一个锁来保护。
内置锁
每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁。线程在进入同步代码块之前自动获取锁,并且在退出同步代码块时自动释放锁。
Java的内置锁是互斥锁。
内置锁是可重入的,因此如果某个线程视图获取一个已经由它自己持有的锁,那么这个请求就会成功。重入意味着获取锁操作的粒度是线程而不是调用。
由于锁能够使其保护的代码路径以串行的方式来访问,因此可以通过锁来构造一些协议以实现对共享状态的独占访问。