syschronized getter和setter但需要避免死锁

时间:2013-10-22 14:25:15

标签: java multithreading

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

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

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

如果我需要增加值,我会使用像

这样的东西
setValue(getValue() + 1);

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

5 个答案:

答案 0 :(得分:5)

  

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

除非涉及两个锁,否则它不会死锁(参见deadlock definition)。但是,当调用get时, 将<{3>},然后调用setter。例如,两个线程可以一个接一个地调用getValue()值方法,然后返回一个接一个地调用setValue(...)。然后第二个setter将覆盖另一个setter的增量。

  1. thread-a调用getValue()等于1
  2. thread-b调用getValue()等于1
  3. thread-a递增值
  4. thread-a使用递增值2
  5. 调用setValue(...)
  6. thread-b递增值
  7. thread-b使用递增值2
  8. 调用setValue(...)

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

    继续使用代码,您需要使用synchronized static void increment()方法 get,increment和set。您需要synchronized,因为++不是原子操作。

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

    所有这些都说,你应该考虑使用AtomicInteger来代替你处理增量和内存同步的竞争条件。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

  

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

没有。出于几个原因。

  1. 尝试获取已存在的原始锁时,线程无法死锁。原始锁是可重入的。尝试获取您已经持有的锁定不会阻止。相反,它只是递增锁定中的计数...将通过相应的锁定释放操作递减。

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

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


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

答案 3 :(得分:0)

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

volatile private Object var;

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

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

this link可以帮助您解决易失性关键字

答案 4 :(得分:0)

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