代码之家  ›  专栏  ›  技术社区  ›  James McMahon

在多线程(Java或.Net)程序中,我可以假设复制变量是原子的吗?

  •  6
  • James McMahon  · 技术社区  · 16 年前

    假设我有一个大型数组或某种集合,由程序的一个组件管理,我们称之为组件监视器。它的工作是定期检查收集的物品是否“脏”。E最近已更改,如果已更改,请将快照写入磁盘(这是为了在发生崩溃时检查应用程序),并再次将其标记为干净。

    在不同线程中运行的同一程序的其他组件调用监视器的方法向数组/集合中添加数据或修改数据。这些方法将集合标记为脏。

    现在,改变方法在其他组件的线程中运行,对吗?如果我没那么幸运,可能会在快照写入磁盘时调用它们,更改已经写入的数据,设置脏标志,然后监视器的线程将其取消设置,而没有保存更改(更改时它已经超过元素)。所以我有一个肮脏的收藏被标记为干净。

    有一段时间,我认为我可以通过制作一个集合的临时副本,将其标记为干净,然后对副本进行序列化来解决这个问题。但是复制是原子的吗,我想。E我可以确定在复制收藏时不会更改它吗?

    • 在开始写入磁盘之前设置锁定标志,并使数据更改方法等待,直到该标志取消设置

    我认为锁定标志可能是最好的方式。 但我还是很好奇:复制是一个原子变量吗?


    字体也许这应该是一个自己的问题,但事实上它是非常相似的。根据下面的答案,我的“锁定标志”方法也可能不起作用,对吗?因为数据更改方法可能会在将锁定标志设置为“锁定”值时检查锁定标志,并确定其未锁定。所以我需要一个特殊的结构,比如互斥体,如果我真的想做对的话,对吗?


    荣誉 erickson 为了他的 very helpful answer

    7 回复  |  直到 7 年前
        1
  •  13
  •   Yoni Roit    16 年前

    不。例如,Java中的长变量在32位机器上不是原子的。

    请看这里: http://gee.cs.oswego.edu/dl/cpj/jmm.html ,尤其是“原子性”和“可见性”段落。

        2
  •  6
  •   Community CDub    7 年前

    不,它不是原子的。 See this question 为什么以及怎么做。

        3
  •  4
  •   Rob    16 年前

    java.util.concurrent.atomic -里面可能有一些好东西你可以用。

        4
  •  2
  •   erickson    16 年前

    在JVM上工作时,您需要关注对其他线程的更改的可见性。一般来说,你应该在一段时间内完成作业 synchronized 块,否则该变量应为 volatile java.util.concurrent.atomic 包裹

    但是,在您的情况下,听起来好像只有一个线程清除了“脏”标记—保存数据的线程。如果是这种情况,请清除标志 之前 写数据。如果其他线程设置它 虽然 您正在写入数据,它将保持设置状态,直到下一次计划写入。我会使用 AtomicBoolean

    private final AtomicBoolean dirty = new AtomicBoolean();
    
    /**
     * Any method that modifies the data structure should set the dirty flag.
     */
    public void modify() {
      /* Modify the data first. */
      ...
      /* Set the flag afterward. */
      dirty.set(true);
    }
    
    private class Persister extends Thread {
      public void run() {
        while (!Thread.interrupted()) {
          if (dirty.getAndSet(false)) {
            /* The dirty flag was set; this thread cleared it 
             * and should now persist the data. */
             ...
          }
        }
      }
    }
    
        5
  •  1
  •   Community CDub    7 年前

    设置32位(至少在.NET中)是原子级的,但对您没有好处。你必须阅读它才能知道它是否被锁定,因此你可能会阅读它,在阅读之后,在你可以设置它之前,其他人会阅读它,因此两个线程结束 在…内

    另见: Is accessing a variable in C# an atomic operation?

        6
  •  0
  •   James Anderson    16 年前

    在某些硬件和JVM上,某些副本将是原子的 但更安全的是,假设情况并非如此,即使是简单的整数到整数分配也可以转换为x856硬件上的四条机器指令。

    两个线程可以同时更新。

        7
  •  -2
  •   Carra    16 年前

    据我所知,并不总是这样。

    Int32将是,Int64不在32位系统上,因为它需要2 x 32位。因此,它不适合一个32位单元。