Java并发编程1:线程安全性


作者: 康凯森

日期: 2016-11-05

分类: Java


线程安全的核心在于对状态访问进行管理,特别是对共享的可变的状态进行访问。 换句话说,对于不可变或者不共享的状态进行访问就不会存在线程安全性问题。

共享 意味着变量可以由多个线程访问。 可变 意味着变量的值在其生命周期可能发生变化。

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

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

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

1 什么是线程安全性

多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类始终可以表现出正确的行为,则称这个类是线程安全的。

2 原子性

不可分割的操作,这些操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

并发环境的原子性和是事务应用程序的原子性有着相同的含义—— 一组语句作为一个不可分割的单元被执行。

3 竞态条件

竞态条件: 不恰当的执行顺序会出现不正确的结果

当多个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。

大多数竞态条件的本质——基于一种可能失效的观察结果来做出判断或执行某个计算,这种类型的静态条件称为“先检查后执行”。

4 内置锁

每个Java对象都可以用作一个实现同步的锁,这些锁称为内置锁或者监视器锁

synchronized(lock) {
//访问或者修改由锁保护的共享状态
}

5 重入

重入: 一个线程可以再次获得一个已经由它自己持有的锁。

重入的一种实现方式:

  • 为每个锁关联一个获取计数器和一个所有者线程。
  • 当计数器为0时,认为无人持有这个锁。
  • 当线程请求一个无人持有的锁时,记录下锁的持有者,计数器加1。
  • 如果一个线程再次获得这个锁,计数器将递增。
  • 当线程退出同步代码块时,计数器将递减。
  • 当计数器减为0时,释放这个锁。

6 用锁来保护状态

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

当类的不变性条件涉及多个状态变量时,在不变性条件的每个变量都必须由同一个锁来保护。

当执行时间较长的计算或者可能无法快速完成的操作时(网络IO),一定不要持有锁。

7 参考资料

本文是《Java 并发编程实战》的读书笔记。


评论