Java同步线程

3

不确定这个D类线程是否正确。是否存在竞态条件?当访问 i 时,是否应该在同步块中?如果D是外部类,并且A实例被传递给D构造函数会怎样呢?

class A
{
    int i;
    void f() {
        i++;
        D d = new D();
        d.start();
    }
    class D extends Thread {
        public void run() {
            int g = i;
        }
    }
}

2
完全没有关系,因为其他人似乎已经涵盖了你的问题,但是扩展Thread被认为是不好的做法。 - pickypg
Google开发者在Android示例代码中经常这样做! - Sam Adamsh
3
这是更好的方法。使用这种方法,您不会意外覆盖任何内容,也不会携带不必要的额外内存开销。请参考链接了解具体实现方式。 - pickypg
完全没有问题。在启动线程之前,您会增加i的值,并且没有任何线程会修改i的值。 - xagyg
3个回答

1

是否存在竞态条件?

如果您从多个线程调用 f,则可能存在竞态条件,因为您将访问一个共享变量(i),而没有适当的同步。

但请注意,启动线程会创建 happens-before 关系

因此,g 将等于在启动线程之前的 i 的值(即在调用 d.start() 时的 i 的值)或任何后续值,如果 i 在此期间被另一个线程修改,则可能存在其他值(没有保证实际看到这样的修改)。


1
只要您只调用f一次,这就是安全的。当一个线程A修改数据并且从线程A启动的线程B之间存在Happens-before关系(HB-relationship在Thread.start处),因此在启动D后没有人再修改数据,这是安全的。
破坏线程安全的方法有:
  • 再次更改i,包括再次调用foo
  • 从与D或调用foo的线程不同的线程读取i
即使是从调用foo的线程中进行的变异,您也无法再次更改i,原因是这种变异将发生在d.start()之后,并且因此不会与第二次变异产生HB边缘。
您不能从任意线程中读取i的原因是,该线程无法获得i++变异的明确定义视图。
它可能比那更微妙一些,但是在高层次上,就是这样。

1
如果f()将从同一线程调用,则不会有问题,因为start()保证irun()可见,并且run()不会更改i。否则,您需要使用AtomicIntegersynchronized。请注意,仅使用volatile i将无法解决问题,可能会丢失一些i++

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接