Java线程安全

什么是线程安全性

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
说白了就是:多个线程访问同一变量或对象时,都能保证结果的正确性就是线程安全。

竞态条件

在并发编程中,由于不恰当的执行时序而出现不正确的结果,或者说当某个计算的正确性取决于多个线程的交替执行时序时,那么就会发生竞态条件。也可以说是正确的结果要取决于运气。

串行访问

串行访问意味着多个线程依次以独占的方式访问对象,面不是并发地访问。由于锁能使其保护的代码路径以串行形式来访问,因此可以通过锁来构造一些协议以实现对共享状态的独占访问。只要始终遵循这些协议,就能确保状态的一致性。

原子性

一种不可分割的操作,比如加了 synchronized 的同步代码块、一些原子变量类(AtomicLong等)。由锁保护的同步代码块会以原子方式执行,多个线程同时执行该代码块时也不会相互干扰。

内存可见性

关键字synchronized 不仅能用于实现原子性或者确定临界区,还有另一个重要的方面:内存可见性。可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。volatile变量保证了所有线程的可见性。普通变量与volatile变量的区别是,volatile的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。因此,可以说volatile保证了多线程操作时变量的可见性,而普通变量则不能保证这一点。

有序性

有序性是指:如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。前半句是指“线程内表现为串行的语义”,后半句是指“指令重排序”现象和“工作内存与主内存同步延迟”现象。Java语言提供了volatile和synchronized两个关键字来保证线程之间操作的有序性,volatile关键字本身就包含了禁止指令重排序的语义,而synchronized则是由“一个变量在同一个时刻只允许一条线程对其进行lock操作”这条规则获得的,这条规则决定了持有同一个锁的两个同步块只能串行地进入。

volatile 变量

Java 提供了一种销弱的同步机制,即volatile 变量,用来确保将变量的更新操作通知到其他线程。加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。当且仅当满足以下所有条件时,才应该使用volatile变量:

  • 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。
  • 该变量不会与其他状态变量一起纳入不变性条件中。
  • 在访问变量时不需要加锁。

    使用volatile变量的每二个语义是禁止指令重排序优化,普通的变量仅仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致。因为在一个线程的方法执行过程中无法感知到这点,这也就是Java内存模型中描述的所谓的“线程内表现为串行的语义”。

重排序

Java虚拟机编译器通过指令重排序来对程序运行效率的优化,使编译器和处理器为了提高并行度。但重排序会导致线程安全问题。重排序不会影响单线程环境的执行结果,但是会破坏多线程的执行语义。

as-if-serial语义

as-if-serial 语义的意思指:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵守as-if-serial语义。

为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作可能被编译器和处理器重排序。

as-if-serial语义把单线程程序保护了起来,遵守as-if-serial语义的编译器,runtime 和处理器共同为编写单线程程序的程序员创建了一个幻觉:单线程程序是按程序的顺序来执行的。as-if-serial语义使单线程程序员无需担心重排序会干扰他们,也无需担心内存可见性问题。注意as-if-serial只保证单线程环境,多线程环境下无效。

你可能感兴趣的:(android,java)