可能是一个非常愚蠢的问题。只想证实我的理解。
class Test
{
private volatile String id;
public void setID(String id)
{
this.id = id;
}
public String getID()
{
return id;
}
}
让我们说多个线程可以访问上面一个类的对象。我的理解是,如果像上面那样简单的getter和setter(做简单的初始化),我不需要让这些方法同步,对吗?
我想volatile是必需的,否则id的值可能会在不同的线程中过时。
那么,如果我们没有这些方法同步,任何人都可以看到任何问题吗?
答案 0 :(得分:6)
我的理解是,如果像上面那样简单的getter和setter(做简单的初始化),我不需要让这些方法同步,对吗?
更正,因为它们获取和设置的内容(对象引用)由JVM原子地处理。
答案是“不,你做需要同步”如果您使用的是long
或double
,而没有将其标记为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)
方法很好地更改变量的值。第一个线程将看到此修改后的值。
所以不要混淆使用原子变量和同步函数。