线程安全: 当多个线程访问某个类(对象)时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为,那么就称为这个类(对象)是线程安全的。
线程安全的核心在于要对对象的状态访问操作(read,write)进行管理,特别是对共享的和可变的状态的访问。
对象的状态指的是存储在状态变量(实例或静态域)中的数据。
一个对象是否需要是线程安全的,取决于是否被多个线程访问。要使得对象是线程安全的,需要采用同步机制来协同对对象可变状态的访问。
Java中主要的同步机制有synchronized关键字(独占锁),有同步语义的还有volatile变量,显式锁以及原子变量。
如果当多个线程访问一个可变状态变量并且没有使用正确的同步机制,就会出现错误。有三种方式修复这个错误:
不在线程之间共享该变量状态
将状态变量修改为不可变的变量
在访问状态变量时使用同步
在并发编程中,当某个计算的正确性取决于多个线程执行的时序时,就会出现竞态条件(竞争条件)。比较常见的竞态条件类型就是“先检查后执行(Check-Then-Act)”,即通过一个可能失效的观测结果来决定下一步的行为。
要避免竞态条件,就必须在某个县城修改完可变状态后,通过某种方式防止其他线程使用这个状态,从而确保其他线程只能在修改完成之前或者之后读取和修改状态,而不是在修改过程中。
可以将会产生竞态条件的一组操作以加锁机制保证这一组以原子的形式执行,这一组操作称为复合操作。
要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量。
内置锁
Java的内置锁相当于一种互斥锁
任何一个执行同步代码块的线程,都不能看到有其他线程正在执行由同一个锁保护的同步代码块
内置锁是可重入的,重入意味着获取锁的操作的粒度是县城而不是调用(POSIX互斥体的获取操作是以调用为粒度的)