有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

当我想知道这个问题时,我正担心我正在设计的应用程序中的竞争条件

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

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

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

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

同时,我想我已经找到了更好的解决方案,比如

  • 在开始写入磁盘之前设置锁定标志,并使数据更改方法等待,直到该标志取消设置
  • 让数据更改方法写入“更改队列”,而不是直接写入集合,并让执行磁盘写入过程的线程写入该队列

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


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


我对他对我的后续行动表示赞赏。我真的应该问这两个问题,这样我就可以接受两个答案了。请你也给他投票


共 (6) 个答案

  1. # 1 楼答案

    设置32位(至少在.NET中)是原子级的,但它对您没有好处。你必须阅读它才能知道它是否被锁定,所以你可能会阅读它,在阅读之后,其他人会在你设置它之前阅读它,所以两个线程最后会出现在“受保护”代码中。这正是实际同步对象(如.NET Monitor类)的用途。您还可以使用Interlocked来检查并增加锁变量

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

  2. # 3 楼答案

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

    此外,还有一个“线程缓存”问题——除非变量是可变的或在同步块内,否则另一个线程可能看不到变量值的更改。这适用于所有类型的变量,而不仅仅是长变量

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

  3. # 4 楼答案

    在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. */
             ...
          }
        }
      }
    }
    
  4. # 5 楼答案

    这在很大程度上取决于您运行的硬件和JVM

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

    字符串和数组副本可能涉及数千条指令的序列 两个线程可以同时更新