volatile 关键字好像没什么用?

发布于 2024-09-01 03:02:31 字数 1666 浏览 4 评论 0原文

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class Main implements Runnable {

   private final CountDownLatch cdl1 = new CountDownLatch(NUM_THREADS);
   private volatile int bar = 0;
   private AtomicInteger count = new AtomicInteger(0);

   private static final int NUM_THREADS = 25;

   public static void main(String[] args) {
      Main main = new Main();
      for(int i = 0; i < NUM_THREADS; i++)
         new Thread(main).start();
   }

   public void run() {
      int i = count.incrementAndGet();
      cdl1.countDown();
      try {
         cdl1.await();
      } catch (InterruptedException e1) {
         e1.printStackTrace();
      }
      bar = i;
      if(bar != i)
         System.out.println("Bar not equal to i");
      else
         System.out.println("Bar equal to i");
   }

}

每个Thread进入run方法并通过从AtomicInteger获取值来获取唯一的、线程限制的int变量i code> 称为 count。然后,每个线程等待名为cdl1CountDownLatch(当最后一个线程到达锁存器时,所有线程 已发布)。当闩锁被释放时,每个线程都会尝试将其受限的 i 值分配给名为 bar易失性、int >。

我希望除了一个线程之外的每个线程都会打印出“Bar not equal to i”,但每个线程都会打印“Bar equal to i”。呃,wtf 如果不是这样的话,volatile 真的能做到吗?

每个Thread都尝试在同一时间设置bar的值是有意为之。

编辑:

根据答案,将代码更改为:

...
bar = i;
try {
    Thread.sleep(0);
} catch(InterruptedException e) {
    e.printStackTrace();
}
...

确保在变量的设置和读取之间浪费一点时间。

现在,Bar 的相同/不同值的打印结果为 50/50。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class Main implements Runnable {

   private final CountDownLatch cdl1 = new CountDownLatch(NUM_THREADS);
   private volatile int bar = 0;
   private AtomicInteger count = new AtomicInteger(0);

   private static final int NUM_THREADS = 25;

   public static void main(String[] args) {
      Main main = new Main();
      for(int i = 0; i < NUM_THREADS; i++)
         new Thread(main).start();
   }

   public void run() {
      int i = count.incrementAndGet();
      cdl1.countDown();
      try {
         cdl1.await();
      } catch (InterruptedException e1) {
         e1.printStackTrace();
      }
      bar = i;
      if(bar != i)
         System.out.println("Bar not equal to i");
      else
         System.out.println("Bar equal to i");
   }

}

Each Thread enters the run method and acquires a unique, thread confined, int variable i by getting a value from the AtomicInteger called count. Each Thread then awaits the CountDownLatch called cdl1 (when the last Thread reaches the latch, all Threads are released). When the latch is released each thread attempts to assign their confined i value to the shared, volatile, int called bar.

I would expect every Thread except one to print out "Bar not equal to i", but every Thread prints "Bar equal to i". Eh, wtf does volatile actually do if not this?

It is a deliberate intention that each Thread attempts to set the value of bar at exactly the same time.

EDIT:

In light of the answer, changed code to this:

...
bar = i;
try {
    Thread.sleep(0);
} catch(InterruptedException e) {
    e.printStackTrace();
}
...

To ensure that a little time is wasted between the set and read of the variable.

Now the print is 50/50 on same/different value for Bar.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(5

乖不如嘢 2024-09-08 03:02:32

它不是。你滥用它了。 Herb Sutter 在此处发表了一篇很棒的文章,对此进行了更详细的解释。

基本思想是易失性使变量变得不可优化。它并不使它们线程安全。

It's not. You're misusing it. There is a great article here by Herb Sutter that explains it in more detail.

The basic idea is that volatile makes variables unoptimisable. It does not make them thread safe.

分开我的手 2024-09-08 03:02:32

要回答“Volatile 实际上有什么作用?”:

Volatile 与可见性有关。在Java的线程模型中,如果线程A写入常规共享字段,则不能保证线程B永远看到A写入的值,除非线程B以某种方式同步。易失性是同步机制之一。

与非易失性字段不同,当线程 A 写入易失性字段并且线程 B 随后读取它时,B 一定会看到新值而不是旧版本。

(实际上 volatile 的作用甚至更多 - 线程 B 不仅会看到该字段的新值,还会看到 A 在设置 volatile 变量之前写入的所有其他内容。它建立了发生之前的关系)。

To answer the 'WTF does volatile actually do?':

volatile is all about visibility. In Java's thread model, if a thread A writes into a regular shared field, there is no guarantee that a thread B will ever see the value written by A, unless the threads are synchronized somehow. volatile is one of the synchronization mechanisms.

Unlike non-volatile fields, when thread A writes into a volatile field and thread B later reads it, B is guaranteed to see the new value and not an older version.

(Actually volatile does even more - thread B will not only see the new value of the field, but everything else written by A before it set the volatile variable as well. It established a happened-before relationship).

伴我心暖 2024-09-08 03:02:32

您应该做的是将 volatile int 实例替换为 AtomicInteger。请参阅此处

What you should do is replace your instance of volatile int with AtomicInteger. See here.

任谁 2024-09-08 03:02:32

我认为你的意思是这样写:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class Main implements Runnable {

   private final CountDownLatch cdl1 = new CountDownLatch(NUM_THREADS);
   private volatile int bar = 0;
   private AtomicInteger count = new AtomicInteger(0);

   private static final int NUM_THREADS = 25;

   public static void main(String[] args) {
      Main main = new Main();
      for(int i = 0; i < NUM_THREADS; i++)
         new Thread(main).start();
   }

   public void run() {
      int i = count.incrementAndGet();
      bar = i;
      cdl1.countDown();
      try {
         cdl1.await();
      } catch (InterruptedException e1) {
         e1.printStackTrace();
      }
      if(bar != i)
         System.out.println("Bar not equal to i");
      else
         System.out.println("Bar equal to i");
   }

}

它会像你预期的那样打印“Bar not equal to i”

I think you meant to write this:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class Main implements Runnable {

   private final CountDownLatch cdl1 = new CountDownLatch(NUM_THREADS);
   private volatile int bar = 0;
   private AtomicInteger count = new AtomicInteger(0);

   private static final int NUM_THREADS = 25;

   public static void main(String[] args) {
      Main main = new Main();
      for(int i = 0; i < NUM_THREADS; i++)
         new Thread(main).start();
   }

   public void run() {
      int i = count.incrementAndGet();
      bar = i;
      cdl1.countDown();
      try {
         cdl1.await();
      } catch (InterruptedException e1) {
         e1.printStackTrace();
      }
      if(bar != i)
         System.out.println("Bar not equal to i");
      else
         System.out.println("Bar equal to i");
   }

}

Which prints "Bar not equal to i" like you expected.

乜一 2024-09-08 03:02:31

JVM 决定线程何时运行,而不是您。如果感觉就像握住刚刚释放锁存器的其中一个再保持 10 毫秒,只是因为它可以做到这一点。闩锁释放后,它们仍然需要等待轮到执行。除非您在 25 核计算机上运行它,否则它们不会在机器内部接近“同一时间”的任何地方分配栏。由于您所做的只是几个原始操作,因此在释放下一个操作之前,其中一个操作不可能在其时间片内完成!

The JVM decides when the threads run, not you. If it felt like holding one of the ones whose latch just released for another 10ms, just because, it can do that. After the latch releases, they still have to wait for their turn to execute. Unless you're running it on a 25 core computer, they're not all assigning bar at anywhere near 'the same time' down inside the machine. Since all you're doing is a couple of primitive operations, it's extremely unlikely that one of them won't finish inside its time slice before the next one gets released!

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