竞态可能会在核心代码中法师。如果某部分代码被多个线程执行,如果线程执行的顺序不一样,结果不一样,那么这块代码可以被称为核心代码。
核心代码
举个例子:
public class Counter {
protected long count = 0;
public void add(long value){
this.count = this.count + value;
}
}
add()
方法便是核心代码。
我们先拎清楚,cpu是如何改变count的值的:
- cpu将count从主内存中读取到cpu寄存器中;
- 修改寄存器中的count的值;
- 将寄存器中的count的值写回主内存。
如果ThreadA和ThreadB同时执行以上步骤:
- ThreadA-1, ThreadA-2
- ThreadB-1, ThreadB-2
- ThreadA-3, ThreadB-3
这样会导致ThreadB的值覆盖了ThreadA的值,导致结果不是我们想要的结果。
如何避免竞态
可以使用synchronized或Java的各种锁来避免竞态,这些知识点我们后面再说。
吞吐量
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;
}
}
}
上面的例子中,我们简单的将可能发生竞态的代码块直接加锁,这样子虽然解决了问题,但是由于关键代码块每次只有一条线程能进入,吞吐量变低。
改进方法:
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;
}
}
}
参考:http://tutorials.jenkov.com/java-concurrency/race-conditions-and-critical-sections.html