需要在getter和setter中同步

时间:2013-12-11 10:17:59

标签: java multithreading

可能是一个非常愚蠢的问题。只想证实我的理解。

class Test
{
       private volatile String id;

       public void setID(String id)
       {
             this.id = id;
       }

       public String getID()
       {
             return id;
       }
}

让我们说多个线程可以访问上面一个类的对象。我的理解是,如果像上面那样简单的getter和setter(做简单的初始化),我不需要让这些方法同步,对吗?
我想volatile是必需的,否则id的值可能会在不同的线程中过时。
那么,如果我们没有这些方法同步,任何人都可以看到任何问题吗?

4 个答案:

答案 0 :(得分:6)

  

我的理解是,如果像上面那样简单的getter和setter(做简单的初始化),我不需要让这些方法同步,对吗?

更正,因为它们获取和设置的内容(对象引用)由JVM原子地处理。

答案是“不,你需要同步”如果您使用的是longdouble,而没有将其标记为volatile

JLS中涵盖了这方面的两个方面,§17.7

  

<强> 17.7。双原子和非原子的非原子处理

     

出于Java编程语言内存模型的目的,对非易失性long或double值的单次写入被视为两个单独的写入:每个32位一半写入一次。这可能导致线程从一次写入看到64位值的前32位,而从另一次写入看到第二次32位。

     

volatile和long值的写入和读取始终是原子的。

     

对引用的写入和读取始终是原子的,无论它们是作为32位还是64位实现。

答案 1 :(得分:1)

如果您处于multi-threaded环境中。几个线程可以访问您的数据。读取值(get)很好。但是考虑写入(set),那么您的数据将变得不一致。所以你必须Synchronized

答案 2 :(得分:1)

您不需要同步任何这些函数也不需要使用volatile关键字,设置引用始终是原子的。然而,存在由非同步/非挥发引起的其他问题。

首先:线程A通过getID读取的内容可能不是线程B通过setID写的内容,因为线程A太早或者......

第二:线程A准时,但由于缺乏易失性,它正在读取缓存线程变量而不是实际值。

虽然第一个只能通过外部线程同步或代码体系结构来解决,但第二个问题可能导致基于发生之前问题的问题。请看以下示例:

主题A:

myId.setId(3);
idSet = true;

主题B:

if (idSet) {
  accessData(myId.getId());
}

这看起来是正确的 - 而且有点 - 但在JVM优化步骤中可能发生的事情是先执行idSet = true然后执行myId.setId(3)。因此,在最坏的情况下,线程B在if子句上成功,但随后读取错误的值。将id标记为volatile将解决该问题,因为它可以保证每当id被修改时,之前发生的所有事情都会发生。

解决这个问题的另一种方法是使用不可变类,因此没有setter和id是最终的并且是通过构造函数设置的。

答案 3 :(得分:0)

My understanding is that in case of simple getter and setters like above
(doing simple initialization), I do not need to make these methods
 synchronized, correct ? 

使变量volatile变得简单,确保不存在竞争条件。所有线程都将读取相同的变量值,因为volatile变量直接存储在主内存中,而不是存储在Threads本地缓存中。

然而,一个线程可能会进入public String getID()函数。此时,其他线程可以通过执行public void setID(String id)方法很好地更改变量的值。第一个线程将看到此修改后的值。

所以不要混淆使用原子变量和同步函数。