java线程同步

发布于 2024-08-21 06:41:52 字数 226 浏览 8 评论 0原文

在下面的类中,方法 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

别想她 2024-08-28 06:41:52

它不是线程安全的。 Java 中 longdouble 类型的变量被视为两个单独的 32 位变量。当另一个线程读取两半值时,一个线程可能正在写入并已写入一半值。在这种情况下,读者会看到一个不应该存在的值。

要使此线程安全,您可以将 myVar 声明为 volatile(Java 1.5 或更高版本),或者同时使用 setItgetIt< /code> 已同步

请注意,即使 myVar 是 32 位 int,您仍然可能会遇到线程问题,其中一个线程可能会读取另一个线程已更改的过时值。发生这种情况的原因是该值已被 CPU 缓存。要解决此问题,您需要再次将 myVar 声明为 volatile(Java 1.5 或更高版本),或者同时使用 setItgetIt <代码>同步。

还值得注意的是,如果您在后续的 setIt 调用中使用 getIt 的结果,例如 x.setIt(x.getIt() * 2)< /code>,那么您可能希望在两个调用之间进行同步

synchronized(x)
{
  x.setIt(x.getIt() * 2);
}

如果没有额外的同步,另一个线程可能会更改 getItsetIt 调用导致其他线程的值丢失。

It is not thread-safe. Variables of type long and double 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 as volatile (Java 1.5 or later) or make both setIt and getIt synchronized.

Note that even if myVar was a 32-bit int 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 declare myVar as volatile (Java 1.5 or later) or make both setIt and getIt synchronized.

It's also worth noting that if you are using the result of getIt in a subsequent setIt call, e.g. x.setIt(x.getIt() * 2), then you probably want to synchronize across both calls:

synchronized(x)
{
  x.setIt(x.getIt() * 2);
}

Without the extra synchronization, another thread could change the value in between the getIt and setIt calls causing the other thread's value to be lost.

锦欢 2024-08-28 06:41:52

这不是线程安全的。即使您的平台保证 long 的原子写入,缺乏 synchronized 也可能导致一个线程调用 setIt(),甚至在此调用之后完成后,另一个线程可能可以调用 getIt() 并且此调用可能返回 myVar 的旧值。

synchronized 关键字的作用不仅仅是一个线程对块或方法的独占访问。它还保证第二个线程获知变量的更改。

因此,您要么必须将两种方法标记为“同步”,要么将成员“myVar”标记为“易失性”。

这里有一个关于同步的很好的解释

原子操作不能交错,因此可以使用它们而不必担心线程干扰。然而,这并不能消除所有同步原子操作的需要,因为内存一致性错误仍然是可能的。使用易失性变量可以降低内存一致性错误的风险,因为对易失性变量的任何写入都会与同一变量的后续读取建立先发生关系。这意味着对易失性变量的更改始终对其他线程可见。更重要的是,这还意味着当一个线程读取一个 volatile 变量时,它不仅会看到 volatile 的最新更改,还会看到导致更改的代码的副作用。

This is not thread-safe. Even if your platform guarantees atomic writes of long, the lack of synchronized makes it possible that one thread calls setIt() and even after this call has finished it is possible that another thread can call getIt() and this call could return the old value of myVar.

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 member myVar as volatile.

There's a very good explanation about synchronization here:

Atomic actions cannot be interleaved, so they can be used without fear of thread interference. However, this does not eliminate all need to synchronize atomic actions, because memory consistency errors are still possible. Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable. This means that changes to a volatile variable are always visible to other threads. What's more, it also means that when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change.

淡紫姑娘! 2024-08-28 06:41:52

不,不是。至少,在缺乏原子 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.

绮筵 2024-08-28 06:41:52

不,不是,因为 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.

李不 2024-08-28 06:41:52

它应该是,而且通常是,但不能保证线程安全。不同的内核在 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.

じ违心 2024-08-28 06:41:52

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:

  • making myVar final (but then you can’t mutate it)
  • making myVar volatile
  • use synchronized to accessing myVar
冬天的雪花 2024-08-28 06:41:52

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.

暖树树初阳… 2024-08-28 06:41:52

因为它是只读方法。您应该同步 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文