1 2 3 4 5 6 7 8
| private double value;
public synchronized void setValue(double value) {
this.value = value;
}
public double getValue() {
return this.value;
} |
在上面的例子中,让getter同步有什么意义吗?
- 如果使用Java 1.5或更大,只使用SET和GET(EDCOX1,0,则不必同步),则使双字段易失性将是令人满意的。参见Java.UTI.Acth.Ac原子.Ac原子*类和实践中已经引用的Java并发。
- S106MO对所有Java版本都有作用。
- 如果您只为set和get方法寻找synchronized保护数据,请查看stackoverflow.com/questions/9749746/&hellip中的其他选项;
我认为在实践中引用Java并发是最好的:
It is a common mistake to assume that synchronization needs to be used only when writing to shared variables; this is simply not true.
For each mutable state variable that may be accessed by more than one
thread, all accesses to that variable must be performed with the same
lock held. In this case, we say that the variable is guarded by that
lock.
In the absence of synchronization, the compiler, processor, and runtime can do some downright weird things to the order in which operations appear to execute. Attempts to reason about the order in which memory actions"must" happen in insufflciently synchronized multithreaded programs will almost certainly be incorrect.
通常,您不必对原语那么小心,因此如果这是一个int或boolean,可能是:
When a thread reads a variable without synchronization, it may see a
stale value, but at least it sees a value that was actually placed
there by some thread rather than some random value.
但是,对于64位操作来说,这是不正确的,例如在long或double上,如果它们没有声明volatile:
The Java Memory Model requires fetch and
store operations to be atomic, but for nonvolatile long and double
variables, the JVM is permitted to treat a 64-bit read or write as two
separate 32-bit operations. If the reads and writes occur in different
threads, it is therefore possible to read a nonvolatile long and get
back the high 32 bits of one value and the low 32 bits of another.
Thus, even if you don't care about stale values, it is not safe to use
shared mutable long and double variables in multithreaded programs
unless they are declared volatile or guarded by a lock.
- @法比亚巴尼当然了,我正要加上它
- 更详细地说,如果要在多线程应用程序中获取最新版本的值,getter和setter都应该同步。如果,由于一些非常奇怪的原因,您不关心获取最新版本,并且同步占用了太多时间(这种情况不太可能!)我认为你不需要同步吸气剂。除非在这种情况下-双精度数是64位,所以应该始终同步。
- 不要同意第三段。afaik synchronized对操作的顺序没有任何限制。
- @user949300不同步getter是完全错误的。你可能永远不会观察到VaR的变化。
- @用户949300不仅不能获得最新版本,而且您甚至不知道将获得哪个版本——而且该版本可能在不同线程之间有所不同。这很难被接受…
- @我确实说过,这种情况是极不可能发生的,只有在出于某种奇怪的原因,你儿子不关心你得到的是哪种版本的情况下才适用。我编辑评论说"真的很奇怪"!
- "在缺乏同步的情况下,编译器、处理器和运行时可以对操作的执行顺序做一些完全奇怪的事情"——参见docs.oracle.com/javase/specs/jls/se7/html/&hellip中的示例17.4.5-1(发生在一致性之前);
- 关于long和double,我认为在64位JVM上是不正确的,因为计算单元的大小(64位),在64位JVM上不需要对32位值执行2个单独的操作,因为它可以在一个操作中执行。
- @加桑我认为你是对的,除非有人告诉我,否则我会把它加到答案中。
- @Gasan不管JVM是32位还是64位都是无关的,因为我们在规范级别讨论这个问题。jls将long和double操作指定为非原子操作。但是,所有这些都与新的内存模型定义无关,因为很明显,如果没有正确的happens-before顺序,就没有交易。
- @规范中没有关于synchronized先做一个操作的说法,即happens-before的保证,只是说An unlock on a monitor happens-before every subsequent lock on that monitor是显而易见的。也不排除可能的读写顺序的重新排序。
- @Gasan进入一个synchronized块意味着监视器锁定,离开块意味着监视器锁定被释放。
- @Platzhirsch是的,但这并不意味着导致这种情况的操作不会被重新排序。
- @你就在这里。我一直希望long和double操作在64位JVM上是原子操作,但根据规范,这是不保证的。
- @根据马尔科·托波尼克在他的回答中所说的,普拉茨奇,你可能是对的。
- @普拉齐尔斯奇似乎你也是对的,因为(来自JLS):If an action x synchronizes-with a following action y, then we also have hb(x, y).。
- 我不清楚如果不将读写同步到不稳定的long或double(当多个线程访问它们时),会发生什么?我认为重新排序可能会发生,但是您已经引用了案例的规范:"但是,对于64位操作来说,这是不正确的,例如对于long或double,如果它们没有声明为volatile:"。
- 易变读和同步写是可能的。请阅读SeaCudio.Cur.Org/Curnfs/Studio/Java/Helip;(参见"符合条件的解决方案"(易失性读、同步写))。我在blog.javaslang.com/synchronizedthis中使用过它。
- 所以…我们应该同步所有getter吗?
- @如果它访问共享字段,则为tylerpfaff。
- 马尔科和阿塞利亚斯是对的,投反对票。请修正,我将撤销我的投票。
让我举例说明什么是JIT编译代码的合法方法。你写:
1 2 3 4
| while (myBean. getValue() > 1.0) {
// perform some action
Thread. sleep(1);
} |
JIT编译:
1 2 3 4 5
| if (myBean. getValue() > 1.0)
while (true) {
// perform some action
Thread. sleep(1);
} |
在稍有不同的情况下,即使Java编译器也可以提供类似的字节码(它只需要消除对不同的EDCOX1(5)的动态调度的可能性)。这是一个典型的起重例子。
为什么这是合法的?编译器有权假定执行上述代码时,myBean.getValue()的结果永远不会改变。在没有synchronized的情况下,允许忽略其他线程的任何操作。
- 有没有官方消息说W/O synchronized这样的排列是可能的,synchronized排除了它们的发生?
- @当然,是捷豹路虎。它不会指出任何特定的转换,但这遵循Java内存模型的语义。
- 这可能是真的,但这种排列看起来有点吓人,我认为它会在文档中明确提到。
- @每天你都在执行这样的代码(有些甚至更可怕)。
- 至少我希望这样的"优化"不会发生,如果内环改变了一个检查值。
- @当然,这就是定理证明者的目的。
- 希望能看到一些引用的信息来源。
- @b1naryatrphy您所需要的是jls,特别是内存模型部分。所有允许的优化都是该模型的结果。
- @感谢Markotopolinik,我现在看到了,我很感激。
- 这种优化如何合法?即使在单线程场景中,它也是非法的。编译版本中while循环中的"some action"可能会更改mybean的值。但是,在传递if条件之后,即使该值在while循环中发生了更改,也不会对其进行检查。即使while循环中的值没有更改,并且可以由另一个线程更改,那么在getValue()上同步将如何避免进入无限循环?
- @Ajoybhatia的"一些行动"要么会改变要么不会改变myBean,而JIT将知道这两种方式。如果它确定它可能会改变,那么它就不会进行优化。至于其他线程,这是基本的Java内存模型语义:没有同步/易失性,运行时可以忽略其他线程在推理代码时的动作。
- 如果我的getter是public double getValue() { synchronized(this){return value} },那么jit会不会看不到synchronized并执行相同的操作?
- @cruncher您不必担心JIT进行非法代码转换。
- @Markotopolnik好的。我的问题是,对于JIT来说,是否要进行上述的法律代码转换?如果不是,为什么这是非法的,但对上面的操作码是合法的。这是相同的方法签名。
- @cruncher方法的实现涉及到锁定,而op不涉及锁定,就这么简单。
这里的原因是防止任何其他线程在线程读取时更新该值,从而避免对过时的值执行任何操作。
在这里,get方法将获取"this"上的内部锁,因此任何可能尝试使用setter方法设置/更新的其他线程都必须等待获取"this"上的锁,才能进入执行get的线程已经获取的setter方法。
这就是为什么它建议在对可变状态执行任何操作时遵循使用相同锁的实践。
由于没有复合语句,因此使字段不稳定将在这里工作。
需要注意的是,同步方法使用的是内部锁,即"this"。所以get和set都是同步的意味着任何进入该方法的线程都必须获得这个锁。
在执行非原子64位操作时,应特别考虑。在实践中,Java并发的摘录可能有助于理解这种情况。
"Java内存模型要求获取和存储操作是原子的,但是对于非易失性长和双变量,JVM允许将64位读写作为两个独立的32来对待。位操作。如果读写发生在不同的线程中,那么就有可能读取非易失性long,并返回一个值的高32位和另一个值的低32位。因此,即使你不关心过时的价值观,它除非声明,否则在多线程程序中使用共享可变长变量和双变量是不安全的易挥发或有锁保护。"
也许对于某些人来说,这段代码看起来很糟糕,但它运行得很好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private Double value ;
public void setValue (Double value ){
updateValue (value, true);
}
public Double getValue (){
return updateValue (value, false);
}
private double updateValue (Double value, boolean set ){
synchronized(MyClass. class){
if(set )
this. value = value ;
return value ;
}
} |
- 是的,看起来确实很糟糕。我看不出这比标准的GETT和SETTER简单的同步一行方法要好得多:这是2X以上的代码,因为它看起来非常不寻常(所以它分散了熟悉普通Java实践的读者)。它还在double.class上同步,这有效地使它成为全局锁,而不是每个实例锁。真的有什么意义?
- 请检查一下主题。类同步不阻塞类,只阻塞同步块。有时我们的盖特和塞特人更复杂。在body方法中,我们做不同的事情,我们希望它在许多线程中是安全的。更新函数将暂停另一个线程,例如,如果我们读取一个线程。亲自检查并确保您不会通过简单地同步一个变量来实现这一点。
- 在传递给它的值上的synchronized块中,传递的是double.class,它是JVM中的一个全局对象。
- 在我的例子中,这是一个静态方法。班级不同,例如Double就在这里。也许不是最好的例子,另一类是粘胶。不应该责怪的是,阻塞只针对同步块。
- 可以。我的意思是,这段代码将阻止同一个JVM中的任何其他代码,这个JVM还表示synchronized(double.class)。
- @JKFF-你第一次是对的。这就是我们通常所说的全局锁。这与某些PLS中的全局解释器锁不同。但它更可能是一个瓶颈,而不是一个实例锁。所以从性能的角度来看,这个解决方案绝对是差强人意的…以及"古怪"。