竞争条件和临界区

原文链接:http://tutorials.jenkov.com/java-concurrency/race-conditions-and-critical-sections.html

在同一个应用程序中运行多个线程本身并不会引起问题。当多个线程访问相同的资源时才会出现问题。比如多个线程访问同一块内存区域(变量、数组、或对象)、系统(数据库、 web 服务等)或文件。事实上,只有一个或多个线程改写这些资源时才会出现问题。多个线程只读取而不会改变这些相同的资源时是安全的。

以下面的代码为例来说明多个线程同时执行可能会出现的问题:

    public class Counter {
        protected long count = 0;

        public void add (long value) {
            this.count = this.count + value;
        }
    }

假设一下有两个线程分别为 A 和 B正在执行 Counter 类的同一个实例上的 add 方法。没有途径知道操作系统会在什么时候在这个线程之间切换。这一行代码:

    this.count = this.count + value;

在 JVM 中并不是作为单独的一条指令来执行的。相反,这行代码在 JVM 中的执行过程如下:

    get this.count from memory into register 从内存中把 this.count 加载到寄存器中
    add value to register 把 value 和寄存器中的值相加
    write register to memory 把寄存器中的值写入到内存中

观察一下如果线程 A 和线程 B 按照下面的顺序交替执行会发生什么:

        this.count = 0;
    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

两个线程中的 2 和 3 与 count 相加,两个线程执行完成以后 count 的值应该是 5 。然而,由于两个线程是交替执行的,两个线程从内存中读到的值都是 0 。然后,它们独立的与 2 和 3 相加,最后把结果写回到内存中。 this.count 的结果不是 5 ,而是最后一个线程写入的值。在上面的这种情形下最终结果是线程 A 最后写入的值,但是也可能是线程 B 写入的值。如果没有恰当的线程同步机制的话,完全没有办法知道线程是如何交替执行的。

竞争条件和临界区

两个线程访问同一个资源而且与线程访问资源时的顺序有关的这样一种情形就叫竞争条件。 导致竞争条件发生的代码片段就叫临界区。在前面的例子中, add() 方法导致了竞争条件的发生,它就是临界区。在临界区中可以通过恰当的线程同步来消除竞争条件。

Next:线程安全和共享资源

你可能感兴趣的:(多线程,临界区,竞争条件)