线程安全性

来源:互联网 发布:下载破解软件的软件 编辑:程序博客网 时间:2024/06/04 18:51

1.线程安全的代码其核心在于要对状态访问【对象的状态中指的是存储在实例或者静态域的数据,对象的状态可能还包括其他依赖对象的域,在对象状态中包含了任何可能影响其外部可见行为的数据】操作进行管理,特别是共享的【共享指的是变量可以由多个线程同时进行访问】和可变的【可变的指的是在其生命周期内可以发生变化】状态的访问。

2.一个对象是否需要是线程安全的取决于它是否被多个线程访问。当多个线程同时访问一个可变状态变量时,为了防止出现错误,可以(1)不在线程之间共享该变量(2)把状态变量修改为不可变变量(3)在访问状态变量时使用同步。

3.完全由线程安全类构成的程序并不一定就是线程安全的,线程安全程序并不一定由线程安全类构成,线程安全类也可以包括非线程安全类。线程安全类和线程安全程序没有什么联系。

4.通常会定义各种不变性条件来约束对象的状态,以及定义各种后验条件来描述对象的操作的结果。当多个线程访问某个类时,这个类始终都能表现出争取的行为,那么这个类就是线程安全的。在线程安全类中封装了必要的同步机制。

5.无状态对象一定是线程安全的。

有状态对象:有实例变量的对象,可以保存数据,在不同方法调用间不保留任何状态,即有数据成员的对象。

无状态对象:一次操作,不保存数据,没有实例变量的对象,是不变类,即只有方法没有数据成员的对象,或者有数据成员,但是数据成员是仅可读对象。

6.原子性

原子性操作就是不可再分的操作,即对于访问同一个状态的所有操作来说,这个操作是一个以原子方式执行的操作。

7.竞态条件

当某个计算的正确性取决于多个线程的交替执行时,就会发生竞态条件。最常见的竞态条件类型是“先检查后执行”,即基于一个可能失效的观测结果来决定下一步的动作。

延迟初始化中的竞态条件(懒汉模式的单例)。

8.复合操作

例如“读取-修改-写入”或者“先检查后执行”就是复合操作,当多个线程同时执行复合操作时,就会发生竞态条件,要避免竞态条件,就必须在某个线程修改该变量时,通过某种方式防止其他线程使用这个变量,从而确保其他线程只能在修改操作完成之前或者之后读物或者修改变量状态,而不是在修改状态的过程中。因此要避免竞态条件必须保证操作的原子性。

9.确保操作的原子性

(1)使用线程安全类

java.util.concurrent.atomic中包含了一些原子变量类,用于实现数值和对象引用上的原子状态转换。

(2

在无状态的类中添加一个状态时如果改状态完全由线程安全的对象来管理,那么这个类仍然是线程安全的。

(2)加锁机制

在无状态的类中添加一个状态时可以选择使用上述的线程安全类,单是如果增加多个状态时需要保证不变性条件不被破坏,即多个变量之间不是独立的,某个变量会对其他的数值产生约束,因此当更新某一个变量时,需要在同一个原子操作中对其他变量同时进行更新。

要保护状态的一致性,就需要在单个原子操作中更新所有相关的状态变量。

Java提供了内置锁机制即同步代码块(Synchronized)来支持原子性。同步代码块包括对象锁和类锁。每个Java对象都可以被用作实现一个内置锁,由这个锁保护的同步代码块会以原子的方式执行。

(3)内置锁是可重入的,如果某个线程试图获得一歌已经由它自己持有的锁,那么这个请求会成功。(子类父类)

10.用锁来保护状态

通过锁来构造一些协议来实现对共享状态的独占访问。访问共享状态的复合操作必须是原子操作才能避免竞态条件。如果在复合操作中添加一个锁则会使复合操作变成原子操作。

对于可能被多个线程同时访问的可变状态变量,在访问它时都需要持同一个锁,在这种情况下,我们称状态变量是由这个锁保护的。

对象的内置锁和对象状态之间没有内在的关联,即对象的域不一定要通过内置锁来保护,即当获取与对象关联的锁时,只能阻止其他线程获得同一个锁,但是不能阻止其他线程对该对象的访问。每个共享的和可变的变量应该只由一个锁来保护。

当某个变量由锁来保护时,每次访问该对象都要首先获得锁,即在同一个时刻只有一个线程可以访问这个变量;当类的不变性涉及多个状态变量时,在不变性条件中的每个状态变量必须由同一个锁来保护。

11.活跃性与性能

如果把每个方法都作为同步方法可能导致活跃性问题和性能问题。应该尽量将不影响共享状态而且执行时间较长的操作从同步代码块中分离出去。

原创粉丝点击