【问题标题】:How to Avoid Data Races - Two Examples如何避免数据竞争——两个例子
【发布时间】:2013-12-19 23:12:31
【问题描述】:

有人告诉我,以下代码示例存在数据竞争条件(当然,假设是多个线程):

class C { 
 private int x = 0; 
 private int y = 0; 

 void f() { 
     x = 1; 
     y = 1; 
 } 

 void g() { 
     int a = y; 
     int b = x; 
     assert(b >= a); 
 } 
} 

然而,我被告知以下“修复”没有数据竞争:

class C { 
 private int x = 0; 
 private int y = 0; 

 void f() { 
     synchronized(this) { x = 1; } 
     synchronized(this) { y = 1; } 
 } 

 void g() { 
     int a, b; 
     synchronized(this) { a = y; } 
     synchronized(this) { b = x; } 
     assert(b >= a); 
 } 
}

可以理解,上面的例子还有其他问题,但是我只是想知道为什么第二个代码块没有竞态条件。同步每个赋值语句如何消除数据竞争条件?一次只同步一个赋值语句有什么意义?

澄清一下,数据竞争是这样定义的:

Data races: Simultaneous read/write or write/write of the same memory location

【问题讨论】:

  • 你能解释一下为什么第一个代码有竞争条件吗?

标签: java parallel-processing atomic race-condition


【解决方案1】:

在第一个示例中,data 竞争条件将通过断言失败而被注意到。

那么这怎么可能呢? y > x 应该始终为假,因为y 写在x 之后并在x 之前读取。

即使你考虑所有的交错

            Thread 1                 Thread 2
            ----------------------------------
            read y
            read x
                                     write x 1
                                     write y 1

您应该始终拥有x <= y

但是在安全执行中,如果read vwrite v执行期间,无法保证读取的值.

v is 0
T1 write 1:   wwwwwwwww
T2 read   :         rrrrr 
T3 read   :               rrrrr

在这种情况下,T2 读取的值可以是任何值,例如42。同时保证T3读取的值是1。

在第一种情况下,ab 可以是任何东西,因此断言可能会失败。 “修复”保证永远不会发生数据竞争(并发读\写),并且ab 将始终为 0 或 1。

【讨论】:

  • 在您的示例中,T2 读取的值怎么可能是“任何东西”?这是计算机体系结构的低级概念,涉及变量的分配方式吗?列出一些我可以查找的一般概念会很有帮助。
  • 这就是重点。假设 x 为 0。您对x = 1 唯一了解的是,在 x 之前是零,在 x 之后是一。与during 无关。语言、处理器、编译器提供了明确的方式来添加关于during 的额外保证。 原子性,最强大的就是你可以像没有期间一样行事
【解决方案2】:

谁告诉你这是错的;如果单独同步,竞争条件(在 assert 之前更改 xy;实际上,只是 assert (x >= y); 有同样的问题)仍然存在。

JIT JVM 可能会很好地执行锁粗化并将两对分配移动到单个 synchronized 块中,但语言语义不能保证这一点。

【讨论】:

  • 即使锁粗化另一个线程也无法观察到 b
【解决方案3】:

同步关键字是关于不同线程读取和写入相同的变量、对象和资源。这不是 Java 中的一个小话题,但这里引用 Sun 的一句话:

同步方法启用了一种防止线程干扰和内存一致性错误的简单策略:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都通过同步方法完成。

简而言之:当您有两个线程正在读取和写入同一个“资源”时,例如一个名为 foo 的变量,您需要确保这些线程以原子方式访问该变量。如果没有 synchronized 关键字,您的线程 1 可能看不到线程 2 对 foo 所做的更改,或者更糟的是,它可能只更改了一半。这不会是你在逻辑上所期望的。

【讨论】:

    猜你喜欢
    • 2019-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-15
    • 1970-01-01
    • 2016-09-20
    相关资源
    最近更新 更多