这是我参与8月更文挑战的第20天,活动详情查看:8月更文挑战
竞争条件和临界区
临界区指的是多个线程执行的代码块,由于执行顺序不同会导致结果不同的代码块。当多个线程执行临界区的代码导致结果差异时,则将其称为临界区包含竞争条件。
两个竞争条件
当多个线程以下面两种方式之一对变量进行操作时,便有可能触发竞争条件:
- 读-修改-写
- 检查-操作 具体来看一下这两种模式触发竞争条件的原因。
- 读-修改-写模式意味这多个线程首先读取到变量,随后将变量修改后写入。新的数据则以某种算法依赖于读取到的值。如果两个线程同时读取到数据,同时进行修改数据,再同时将数据写回到内存中,则会产生数据与结果的不统一。
- 检查-修改模式意味着多个给定检查条件,如果两个线程同时检查给定值,然后两个线程都尝试修改该值,便有可能发生这种问题。实际上只有一个线程获取的是原始值,另一个线程可能获取到修改后的值。
读-修改-写临界区
public class Counter {
protected long count = 0;
public void add(long value){
this.count = this.count + value;
}
}
当多个线程同时在执行counter实例的add方法时,无法知道cpu何时从两个线程间进行切换。add方法不会作为原子性进行执行,会分为读、增加、写三个部分。当cpu的切换在两个线程均进行读取后切换,与在其中一个线程执行后第二线程才进行读取,二者的结果存在差异。
检查-操作临界区
如果两个线程执行同一段代码,先对变量status进行检查,检查后如果status为0则进行相应逻辑操作并修改变量。可想而知,由于读取顺序的不同,会导致其中一个线程对逻辑代码的执行结果不同。
防止竞争条件
为了防止发生竞争条件,必须确保临界区作为原子指令执行。这意味着一旦一个线程正在执行它,在第一个线程离开临界区之前,其他线程都不能执行它。也就是说需要利用synchronized关键字或锁等方式保证原子性。
临界区吞吐量
对于较小的临界区,可以对整体代码进行锁定。而对于较大的临界区,需要将锁细化,对局部进行操作。
后记
- 千古兴亡多少事?悠悠。不尽长江滚滚流。