当前位置: 首页 > 知识库问答 >
问题:

使用volatile确保Java中共享(但不是并发)数据的可见性

牛越
2023-03-14

我正在尝试实现LZ77的快速版本,我有一个关于并发编程的问题要问你。

现在我有一个final byte[]buffer和一个final int[]resultHolder,两者的长度相同。该程序执行以下操作:

>

  • 主线程写入所有缓冲区,然后通知线程并等待它们完成。

    单个工作线程处理缓冲区的一部分,并将结果保存在结果保持器的同一部分中。工人部分是排他性的。之后,主线程会收到通知,工作人员会暂停。

    当所有的工作都暂停时,主线程读取结果持有器中的数据并更新缓冲区,然后(如果需要)过程从点1再次开始。

    管理器(主线程)中的重要内容声明如下:

    final byte[] buffer = new byte[SIZE];
    final MemoryHelper memoryHelper = new MemoryHelper(); 
    final ArrayBlockingQueue<Object> waitBuffer = new ArrayBlockingQueue<Object>(TOT_WORKERS);
    final ArrayBlockingQueue<Object> waitResult = new ArrayBlockingQueue<Object>(TOT_WORKERS);
    final int[] resultHolder = new int[SIZE];
    

    MemoryHelper简单地包装了一个易失性字段,并提供了两种方法:一种用于读取该字段,另一种用于写入该字段。

    Worker's run()代码:

    public void run() {
        try {
            // Wait main thread
            while(manager.waitBuffer.take() != SHUTDOWN){
                // Load new buffer values
                manager.memoryHelper.readVolatile();
    
                // Do something
                for (int i = a; i <= b; i++){
                    manager.resultHolder[i] = manager.buffer[i] + 10;
                }
    
                // Flush new values of resultHolder
                manager.memoryHelper.writeVolatile();
                // Signal job done
                manager.waitResult.add(Object.class);
            }
        } catch (InterruptedException e) { }
    }
    

    最后,主线程的重要部分:

    for(int i=0; i < 100_000; i++){
        // Start workers
        for (int j = 0; j < TOT_WORKERS; j++)
            waitBuffer.add(Object.class);
        // Wait workers
        for (int j = 0; j < TOT_WORKERS; j++)
            waitResult.take();
    
        // Load results
        memoryHelper.readVolatile();
        // Do something
        processResult();
        setBuffer();
        // Store buffer
        memoryHelper.writeVolatile();
    }
    

    ArrayBlockingQueue上的同步运行良好。我的疑问是在使用readVolatile()WriteVolatile()。我被告知,写入易失性字段会刷新所有先前更改的数据,然后从另一个线程读取这些数据,使它们可见。

    那么,在这种情况下,确保正确的可见性就足够了吗?从来没有对相同内存区域的真正并发访问,所以易失性字段应该比读写锁便宜得多。

  • 共有1个答案

    楚天宇
    2023-03-14

    这里甚至不需要volatile,因为BlockingQueues已经提供了必要的内存可见性保证:

    内存一致性效果:与其他并发集合一样,在将对象放入BlockingQueue之前,线程中的操作发生在从另一个线程中的BlockingQueue中访问或删除该元素之后的操作之前。

    一般来说,如果您已经有了某种同步,您可能不需要做任何特殊的事情来确保内存可见性,因为它已经由您使用的同步原语保证了。

    但是,当您没有显式同步时(例如在无锁算法中),可以使用volatile读写来确保内存可见性。

    附言。

    此外,它看起来可以使用CyclicBarrier而不是队列解决方案,它是专为类似场景设计的。

     类似资料:
    • 问题内容: 根据: http://www.ibm.com/developerworks/library/j-jtp03304/ 在新的内存模型下,当线程A写入易失性变量V,并且线程B从V读取时,现在保证了在写入V时A可见的任何变量值对B可见。 互联网上的许多地方都指出,以下代码永远不应显示“错误”: 应 当为1,所有的线程是1。 但是 有时我会打印“错误” 。这怎么可能? 问题答案: 更新: 对于

    • 根据: http://www.ibm.com/developerworks/library/j-jtp03304/ 在新的内存模型下,当线程A写入易失性变量V,线程B从V读取时,在写入V时对A可见的任何变量值现在都保证对B可见 互联网上的许多地方声明以下代码永远不应该打印“错误”: 当为1时,所有线程的应为1。 然而,我有时会打印“错误”。这怎么可能呢?

    • volatile修饰的变量在各个线程的工作内存中不存在一致性的问题(在各个线程工作的内存中,volatile修饰的变量也会存在不一致的情况,但是由于每次使用之前都会先刷新主存中的数据到工作内存,执行引擎看不到不一致的情况,因此可以认为不存在不一致的问题),但是java的运算并非原子性的操作,导致volatile在并发下并非是线程安全的。

    • 我正在尝试制作一个JFrame,其中包含一个JGroup,不可见但保持JGroup可见。我怎么才能做到这一点?提前感谢!

    • 我正在创建一个番茄工作计时器应用程序。它有2项活动: 主要活动是设置时间 我已经创建了一个对象,它保存了设置的时间,我希望这些时间显示在Recents活动中的按钮中。但是,似乎没有保存数据,而是显示了包名称。

    • 当程序中有共享数据,肯定不想让其陷入条件竞争,或是不变量被破坏。那么,将所有访问共享数据结构的代码都标记为互斥岂不是更好?这样任何一个线程在执行这些代码时,其他任何线程试图访问共享数据结构,就必须等到那一段代码执行结束。于是,一个线程就不可能会看到被破坏的不变量,除非它本身就是修改共享数据的线程。 当访问共享数据前,使用互斥量将相关数据锁住,再当访问结束后,再将数据解锁。线程库需要保证,当一个线程