0

我已经同步了静态 getter 和 setter,例如:

public synchronized static int getValue() {
    return value;
}

public synchronized static void setValue(int Val) {
    value = Val;
}

如果我需要增加价值,我会使用类似的东西

setValue(getValue() + 1);

可能有一个线程设置“值”并立即尝试获取“值”。在这种情况下会发生死锁吗?如果是,那么如何避免呢?

4

5 回答 5

5

可能有一个线程设置“值”并立即尝试获取“值”。在这种情况下会发生死锁吗?如果是,那么如何避免呢?

除非涉及两个锁,否则它不会是死锁(参见死锁定义)。但是,当调用 get 然后调用 setter 时,将遇到竞争条件。例如,两个线程可以一个接一个地调用getValue()value 方法,然后一个接一个地返回调用setValue(...)。然后第二个 setter 将覆盖另一个 setter 的增量。

  1. thread-a 调用getValue()等于 1
  2. thread-b 调用getValue()等于 1
  3. thread-a 增加值
  4. thread-a 调用setValue(...),增量值为 2
  5. thread-b 增加值
  6. setValue(...)增量值为 2 的线程 b 调用

所以答案应该是 3,但由于竞争条件,它将是 2。

继续您的代码,您需要有一个synchronized static void increment()方法可以同时执行 get、increment 和 set。你需要是synchronized因为++不是原子操作。

public synchronized static int increment() {
    value++;
}

综上所述,您应该考虑使用一种AtomicInteger替代方法,它可以为您处理增量和内存同步的竞争条件。

于 2013-10-22T14:26:58.100 回答
1

synchronized使用与锁相同的对象(类本身)。所以在这种情况下永远不会发生死锁。

于 2013-10-22T14:30:14.950 回答
1

可能有一个线程设置“值”并立即尝试获取“值”。在这种情况下会发生死锁吗?

不,有几个原因。

  1. 线程在尝试获取它已经拥有的原始锁时不能死锁。原始锁是可重入的。尝试获取您已经持有的锁不会阻塞。相反,它只是增加锁中的计数......这将通过相应的锁释放操作而减少。

  2. 在这种情况下,您甚至不会重新进入锁。执行语句时,首先调用getValue()which 获取并释放锁。然后你调用setValue(int)which 再次获取并释放锁。如您所见,执行不会在持有相同锁的同时尝试获取锁。

  3. 事实上,死锁(带有原始锁)至少需要两个不同的线程和两个不同的锁。


最后,正如@Gray 指出的那样,setValue(getValue() + 1)不会可靠地增加值......除非您在持有锁的同时执行该序列。getValue()在和调用之间有一个时间窗口setValue(),另一个线程可以在其中更改值。

于 2013-10-22T14:54:49.803 回答
0

在这种情况下,不需要同步方法,只需将value变量标记为volatile

volatile private Object var;

但是对于读取修改更新,您必须同步对象,但要注意同步方法,因为它接受非空对象引用(不允许使用基元)所以代码会是这样的

volatile static private int value;
public int getValue(){return value;}
public void setValue_direct(int id){value=val;}
public synchronized void setValue_increment(){value++;}

此链接将帮助您了解 volatile 关键字

于 2013-10-22T14:52:27.523 回答
0

如果需要增加值,请声明单独的同步方法。或者使用 AtomicInteger 而不是 int 变量(这样就不需要同步 getter/setter/incrementor)。

于 2013-10-23T08:47:16.133 回答