java线程同步
在下面的类中,方法 getIt()
线程安全吗?为什么?
public class X {
private long myVar;
public void setIt(long var){
myVar = var;
}
public long getIt() {
return myVar;
}
}
In the class below, is the method getIt()
thread safe and why?
public class X {
private long myVar;
public void setIt(long var){
myVar = var;
}
public long getIt() {
return myVar;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
它不是线程安全的。 Java 中
long
和double
类型的变量被视为两个单独的 32 位变量。当另一个线程读取两半值时,一个线程可能正在写入并已写入一半值。在这种情况下,读者会看到一个不应该存在的值。要使此线程安全,您可以将
myVar
声明为volatile
(Java 1.5 或更高版本),或者同时使用setIt
和getIt< /code>
已同步
。请注意,即使
myVar
是 32 位int
,您仍然可能会遇到线程问题,其中一个线程可能会读取另一个线程已更改的过时值。发生这种情况的原因是该值已被 CPU 缓存。要解决此问题,您需要再次将myVar
声明为volatile
(Java 1.5 或更高版本),或者同时使用setIt
和getIt <代码>同步。
还值得注意的是,如果您在后续的
setIt
调用中使用getIt
的结果,例如x.setIt(x.getIt() * 2)< /code>,那么您可能希望在两个调用之间进行
同步
:如果没有额外的同步,另一个线程可能会更改
getIt
和setIt 调用导致其他线程的值丢失。
It is not thread-safe. Variables of type
long
anddouble
in Java are treated as two separate 32-bit variables. One thread could be writing and have written half the value when another thread reads both halves. In this situation, the reader would see a value that was never supposed to exist.To make this thread-safe you can either declare
myVar
asvolatile
(Java 1.5 or later) or make bothsetIt
andgetIt
synchronized
.Note that even if
myVar
was a 32-bitint
you could still run into threading issues where one thread could be reading an out of date value that another thread has changed. This could occur because the value has been cached by the CPU. To resolve this, you again need to declaremyVar
asvolatile
(Java 1.5 or later) or make bothsetIt
andgetIt
synchronized
.It's also worth noting that if you are using the result of
getIt
in a subsequentsetIt
call, e.g.x.setIt(x.getIt() * 2)
, then you probably want tosynchronize
across both calls:Without the extra synchronization, another thread could change the value in between the
getIt
andsetIt
calls causing the other thread's value to be lost.这不是线程安全的。即使您的平台保证
long
的原子写入,缺乏synchronized
也可能导致一个线程调用setIt()
,甚至在此调用之后完成后,另一个线程可能可以调用getIt()
并且此调用可能返回myVar
的旧值。synchronized
关键字的作用不仅仅是一个线程对块或方法的独占访问。它还保证第二个线程获知变量的更改。因此,您要么必须将两种方法标记为“同步”,要么将成员“myVar”标记为“易失性”。
这里有一个关于同步的很好的解释:
This is not thread-safe. Even if your platform guarantees atomic writes of
long
, the lack ofsynchronized
makes it possible that one thread callssetIt()
and even after this call has finished it is possible that another thread can callgetIt()
and this call could return the old value ofmyVar
.The
synchronized
keyword does more than an exclusive access of one thread to a block or a method. It also guarantees that the second thread is informed about a change of a variable.So you either have to mark both methods as
synchronized
or mark the membermyVar
asvolatile
.There's a very good explanation about synchronization here:
不,不是。至少,在缺乏原子 64 位内存访问的平台上是这样。
假设线程 A 调用 setIt,将 32 位复制到后备值所在的内存中,然后在复制其他 32 位之前被抢占。
然后线程 B 调用 getIt。
No, it's not. At least, not on platforms that lack atomic 64-bit memory accesses.
Suppose that Thread A calls
setIt
, copies 32 bits into memory where the backing value is, and is then pre-empted before it can copy the other 32 bits.Then Thread B calls
getIt
.不,不是,因为 long 在 java 中不是原子的,所以一个线程可以在 setIt 方法中写入 long 的 32 位,然后 getIt 可以读取该值,然后 setIt 可以设置其他 32 位。
所以最终的结果是 getIt 返回一个永远无效的值。
No it is not, because longs are not atomic in java, so one thread could have written 32 bits of the long in the setIt method, and then the getIt could read the value, and then setIt could set the other 32 bits.
So the end result is that getIt returns a value that was never valid.
它应该是,而且通常是,但不能保证线程安全。不同的内核在 CPU 缓存中具有不同的版本,或者存储/检索对于所有架构来说都不是原子的,可能会出现问题。使用
AtomicLong
类。It ought to be, and generally is, but is not guaranteed to be thread safe. There could be issues with different cores having different versions in CPU cache, or the store/retrieve not being atomic for all architectures. Use the
AtomicLong
class.getter 不是线程安全的,因为它不受任何保证最新可见性的机制的保护。您的选择是:
myVar
成为最终的(但随后您无法改变它)myVar
成为易失性myVar
The getter is not thread safe because it’s not guarded by any mechanism that guarantees the most up-to-date visibility. Your choices are:
myVar
final (but then you can’t mutate it)myVar
volatilemyVar
AFAIK,现代 JVM 不再分割长操作和双操作。我不知道有任何参考资料表明这仍然是一个问题。例如,请参阅 AtomicLong,它在 Sun 的 JVM 中不使用同步。
假设您想确定这不是问题,那么您可以使用同步 get() 和 set()。但是,如果您正在执行像 add 这样的操作,即 set(get()+1) ,那么这种同步不会给您带来太多好处,您仍然必须同步整个操作的对象。 (解决此问题的更好方法是对同步的 add(n) 使用单个操作)
但是,更好的解决方案是使用 AtomicLong。这支持原子操作,如获取、设置和添加,并且不使用同步。
AFAIK, Modern JVMs no longer split long and double operations. I don't know of any reference which states this is still a problem. For example, see AtomicLong which doesn't use synchronization in Sun's JVM.
Assuming you want to be sure it is not a problem then you can use synchronize both get() and set(). However, if you are performing an operation like add, i.e. set(get()+1) then this synchronization doesn't buy you much, you still have to synchronize the object for the whole operation. (A better way around this is to use a single operation for add(n) which is synchronized)
However, a better solution is to use an AtomicLong. This supports atomic operations like get, set and add and DOESN'T use synchronization.
因为它是只读方法。您应该同步
set
方法。编辑:我明白为什么 get 方法也需要同步。菲尔·罗斯的解释做得很好。
Since it is a read only method. You should synchronize the
set
method.EDIT : I see why the get method needs to be synchronized as well. Good job explaining Phil Ross.