Skip to content

Instantly share code, notes, and snippets.

@lqt0223
Last active March 15, 2017 15:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lqt0223/96194b4c9d9f556d2a0e4df5bf3fca1b to your computer and use it in GitHub Desktop.
Save lqt0223/96194b4c9d9f556d2a0e4df5bf3fca1b to your computer and use it in GitHub Desktop.
06 Thread Interference - from Oracle official documentation

Oracle官方文档节译:Java并发 - 线程干扰

小白初学Java的多线程编程,一下子遇到了海量的新知识点。一方面,Java中要想灵活使用多线程编程,需要了解concurrecy, Thread 等包和类中的很多实现的类的使用方法;另一方面,自己之前学习的比较多的JS是一个单线程的世界,很多多线程背后的原理我是不能理解的。所以这次的文章,用翻译的形式来细读一些基础的Java多线程编程的教程,顺便分享给大家,希望对各位能有帮助。

以下内容翻译自Thread Interference (The Java™ Tutorials > Essential Classes > Concurrency)

线程间干扰

假设有一个简单的类叫做Counter

class Counter {
    private int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }

}

正常情况下,调用Counter 类中的方法 increment 会使c加1,调用decrement则会使c减1。

但如果一个Counter对象被多个线程所访问,就有可能产生线程间的干扰,导致出现不正确的结果。

当两个位于不同线程中的操作,交织(interleave)于同一个数据时,线程间干扰就发生了。也就是说,这两个操作本身是由N个步骤组成,这些步骤相互之间有交织。

表面上看来,对于Counter实例的这些操作是不可能产生交织的。但实际上,即使是看似简单的一个步骤,也会被虚拟机转换成多个步骤。例如,c++这个表达式就可以分解成以下3步:

  1. 获得当前c的值。
  2. 将c的值增加1。
  3. 将结果存储回c。

c--也可以通过同样的方式分解。

假设线程A和线程B在几乎同样的时间分别调用了increment和decrement方法,c的初值为0。则它们的交织后的动作,有可能变成下面这样:

  1. 线程A:获得c的值。
  2. 线程B:获得c的值。
  3. 线程A:增加c的值,结果为1。
  4. 线程B:减少c的值,结果为-1。
  5. 线程A:将结果存储回c,现在c的值为1。
  6. 线程B:将结果存储回c,现在c的值为-1。

线程A的调用产生的结果丢失,被线程B覆盖。上面这种操作交织,只是可能发生的一种情况。也有可能是B的结果丢失,或者最终得到正确的结果。因为这种不确定性,线程间干扰的debug变得困难。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment