天天看点

并发编程五:竞态条件与临界区并发编程:竞态条件与临界区

并发编程:竞态条件与临界区

介绍

当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竟态条件发生的代码称作临界区。

备注:以下这段代码就存在竞态条件,其中return ++count就是临界区。
           
public class Obj
{

    private int count;

    public int incr()
    {
        return ++count;
    }

}
           
同一程序中运行在多个线程本身不会导致任何问题,问题在于当多个线程访问了相同资源时候。 
如:访问同一内存区(变量、数组、对象)、数据库或文件。那么就可能会出现问题了。
           

竞态条件分析

演示代码

public class Counter
{

    private int count = 0;

    public int incr()
    {
        return (++count);

        //上面一句代码替换为下面的代码可能更好理解
        int sum=count+1
        return sum

        //首先++count并非是一个原子操作,其实先读取count值然后再自增操作的。
    }
}
           
假如现在有A,B两个线程执行incr方法

T1时刻:A读取count值为0在执行自增操作之前,cpu又调度到B执行了
T2时刻:B读取count值为0执行自增操作count变为1,cpu又调度到A线程执行
T3时刻:A继续执行自增操作,count值变为1.此时就存在问题了,A线程没有重新读取count的值,导致结果不对
           
JVM按照下面的顺序执行incr()方法
1、从内存获取this.count的值放到寄存器
2、将寄存器中的值加1
3、将寄存器的值写回到内存

如果A、B线程交错执行的情景可能就如下
A:读取this.count到一个寄存器X1(0)
B:读取this.count到一个寄存器X2(0)
B:将寄存器X2的值加1
B:回写寄存器X2(1)到内存,this.count现在就是1
A:将寄存器X1的值加1
A:回写寄存器值X1(1)到内存,this.count闲杂就是1

备注:这样就存在问题了,B线程已经自增过一次了,但是A线程并没有发现,导致它任然以为this.count为0的基础上自增。

因此如果没有采用合适的同步机制,线程间的交叉执行的情况就无法预料的。


备注:之所以存在这种问题,其实本质上市因为操作系统架构问题,任何操作都会经过内存再到寄存器, 
正因为有内存这一环节导致有些时候数据并不及时被刷新,但是有内存的架构师必须的也是很有必要的。如果直接将数据放在寄存器操作的话那简直难以想象。
           

总结

多线程开发是最重要的事情是先分析清楚共享资源和共享对象,明白什么地方会存在安全隐患,这样才能更好的避免引起安全问题。
           

参考

1、http://tutorials.jenkov.com/java-concurrency/thread-safety.html