Java爱好者,我的网站: kailuncen.me/about/html
这几天学习并发编程,race-conditions-and-critical-sections,翻译一下,写点本身的笔记并加上点我的的理解。java
网页中里中提到两个名词Race Condition 和 Critical Section,接下来对他们进行解释和例子演示。编程
在多线程场景下,当多个线程访问同一块资源,且执行结果与线程访问的前后顺序相关,即代表这里面存在着Race Condition,中文翻译即竞争条件。bash
看下面👇的代码,多个线程都会调用add方法对同一个count值进行加法。多线程
public class Counter {
protected long count = 0;
public void add(long value){
this.count = this.count + value;
}
}复制代码
然而,add方法中的加法须要好几个步骤才能完成。并发
1. 从内存中读取count的值到寄存器。
2. 加value。
3. 写回内存。复制代码
若是有两个线程都对add方法进行了操做,好比线程A加3,线程B加2,咱们的预期结果是5。因为线程的访问顺序以及切换的时间是不可预期的,在特定的访问顺序下,可能出现一些出乎意料的结果,好比下文中的执行顺序。学习
A: Reads this.count into a register (0)
B: Reads this.count into a register (0)
B: Adds value 2 to register
B: Writes register value (2) back to memory. this.count now equals 2
A: Adds value 3 to register
A: Writes register value (3) back to memory. this.count now equals 3复制代码
因为加法不是原子性的,在加法执行过程当中的每一步均可能存在着线程切换。
好比线程A和B都前后读到0,而后线程B占用了时间片完成了加2的操做,写回了内存,此时内存中count的值等于2。
而后线程A从新获得调度,此时线程A内部的count值仍是0,线程A对主内存内count的变化是不可见的,而后线程完成加3操做,写回内存,此时count值等于3。网站
上述代码中的add方法内部就存在着竞争条件,会根据线程执行顺序的不肯定性影响最后的执行结果。this
咱们把会致使Race Condition的区域称为Critical Section,中文翻译临界区。临界区即每一个线程中访问临界资源的那段代码。spa
在上文的代码中,this.count就是临界资源
this.count = this.count + value复制代码
就是临界区,为了保证执行结果的正确性,避免临界区内产生竞争条件,咱们须要确保临界区内的执行是原子的,每次仅容许一个线程进去,进入后不容许其余线程进入。
咱们能够采用线程同步作到以上的要求,线程同步能够使用synchronized同步代码,或者locks,或者是原子变量好比AtomicInteger等。
能够把整个临界区使用synchronized同步,但把临界区拆分红多个小的临界区可以下降对共享资源的争夺,增长整个临界区的吞吐量,下面举个例子。
public class TwoSums {
private int sum1 = 0;
private int sum2 = 0;
public void add(int val1, int val2){
synchronized(this){
this.sum1 += val1;
this.sum2 += val2;
}
}
}复制代码
在上述代码中,简单的作法就是锁住整个对象,只有一个线程可以执行两个不一样变量的加法操做。然而,因为这两个变量是互相独立的,能够拆分到两个不一样的synchronized块中。
public class TwoSums {
private int sum1 = 0;
private int sum2 = 0;
private Integer sum1Lock = new Integer(1);
private Integer sum2Lock = new Integer(2);
public void add(int val1, int val2){
synchronized(this.sum1Lock){
this.sum1 += val1;
}
synchronized(this.sum2Lock){
this.sum2 += val2;
}
}
}复制代码
改动后,两个线程能够同时在add方法中操做,一个线程在第一个synchronized块,另外一个线程在第二个synchronized块,两个synchronized块同步的是不一样的对象,因此两个线程能够独立执行,总体线程等待的时间会变少,吞吐量可以获得提高。