第二章 线程安全性

2.0  简介 

  线程安全的核心: 对 对象状态(包括实例和静态域)操作的访问顺序进行管理.

  Java 的主要同步机制:

  • synchronize(独占的加锁方式)
  • volatile(值被修改后立即对其他线程可见)
  • Lock(显示锁, 提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作)
  • 原子变量

  如何使用多个线程安全的访问某个变量: 

  • 在不同线程间共享该变量
  • 访问变量时进行同步

2.1 什么是线程安全性

  定义: 当多个线程访问某个类时, 这个类始终都能表现出正确的行为.

  核心概念---正确性: 

  某个类的行为与其规范完全一致,良好的规范中会定义各种不变性条件约束对象的状态,以及定义各种后验条件描述对象的操作结果.

  无状态对象: 既不包含任何域, 也不包含任何对其他类中域的引用. 

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

 

2.2 原子性

  定义: 一个操作要么完整的被执行,要么完全不被执行. 

2.2.1 竞态条件

  定义: 由于不恰当的执行顺序而导致出现不正确的结果.

  常见类型:

  • 先检查后执行. 通过一个可能失效的观测结果来决定下一步的操作. 比如延迟初始化.

2.2.2 复合操作

  定义: 任务在执行过程中可以被打断的一序列操作.

  比如 先检查后执行,读取-修改-写入

2.2.3 原子操作

  定义: 任务在执行过程中不会被打断的一序列操作.

2.3 加锁机制

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

2.3.1  内置锁

  同步代码块(Synchronized Block),主要由两部分构成.

  • 一部分(lock)作为锁的对象引用
  • 另一部分作为这个锁保护的代码块 
synchronized (lock) {
    //代码块     
}

  以synchronize修饰的方法为同步方法,实例方法的锁就是调用方法的对象,静态方法的锁就是方法所在的类对象(Class对象)

  每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁. 进入方法前自动获得锁,退出方法后自动释放锁. 

  内置锁相当于一种互斥体(或者互斥锁),意味着最多只有一个线程能持有这种锁.

2.3.2  重入锁

  定义: 线程试图获得一个已经由自己持有的锁.

  内置锁可重入的. 

  可以解决 死锁问题. 

  重入 意味着获取锁的操作的粒度线程级别

  实现方式: 为每个锁关联一个计数值和一个所有者线程. 当计数值为0时表示没有被任何线程持有. 当一个线程请求一个未被持有的锁时,JVM将记录下锁的持有者,并将计数器的值置为1,当同一个线程再次获取这个锁,计计数值将递增,当退出同步代码块时相应的递减. 计数值为0时,这个锁将被释放.

2.4 用锁来保护状态

  锁能使其保护的代码以串行形式访问.  也可以通过锁构造一些协议实现对共享状态的独占访问(比如对某个类的全部方法添加synchronized).

  当使用锁来保护对某个变量的访问时,那么在访问变量的所有位置上都要使用同一个锁. 此时这个状态变量有这个锁保护.

  每个共享和可变的变量都应该由一个锁来保护.

  当获取与对象关联的锁时,只能阻止其他线程获得同一个锁,而不能阻止其他线程访问该对象.

  当类的不变性条件涉及多个状态变量时,不变性条件的每个变量必须由同一个锁来保护.因此可以在单个原子操作中访问或更新这些变量.确保不变性条件不被破坏.

原文地址:https://www.cnblogs.com/virgosnail/p/9445980.html