首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基本数组写入的Java并发可见性

基本数组写入的Java并发可见性
EN

Stack Overflow用户
提问于 2013-02-05 18:58:40
回答 4查看 1.3K关注 0票数 14

我最近在我的代码库中找到了这个宝石:

代码语言:javascript
复制
/** This class is used to "publish" changes to a non-volatile variable.
 *
 * Access to non-volatile and volatile variables cannot be reordered,
 * so if you make changes to a non-volatile variable before calling publish,
 * they are guaranteed to be visible to a thread which calls syncChanges
 *
 */
private static class Publisher {
    //This variable may not look like it's doing anything, but it really is.
    //See the documentaion for this class.
    private volatile AtomicInteger sync = new AtomicInteger(0);

    void publish() {
        sync.incrementAndGet();
    }

    /**
     *
     * @return the return value of this function has no meaning.
     * You should not make *any* assumptions about it.
     */
    int syncChanges() {
        return sync.get();
    }
}

这一用途如下:

螺纹1

代码语言:javascript
复制
float[][] matrix;
matrix[x][y] = n;
publisher.publish();

螺纹2

代码语言:javascript
复制
publisher.syncChanges();
myVar = matrix[x][y];

线程1是一个不断运行的后台更新线程。线程2是一个HTTP工作线程,它不关心它读取的内容在任何方面是一致的或原子的,只是写“最终”到达那里,并且不会丢失作为并发神的供品。

现在,这触发了我所有的警钟。自定义并发算法在不相关代码中编写得很深。

不幸的是,修复代码并不简单。Java对并发基元矩阵的支持不是很好。解决这个问题的最清晰的方法似乎是使用ReadWriteLock,但这可能会对性能产生负面影响。显然,正确性更重要,但在将其从性能敏感区域中删除之前,我似乎应该证明这是不正确的。

根据java.util.concurrent文档,以下内容创建了happens-before关系:

线程中的每个动作都会发生--在该线程中的每个动作按程序的顺序进行之前。 对易失性字段的写入发生在随后对该字段的每一次读取之前。写入和读取易失性字段与进入和退出监视器具有类似的内存一致性效果,但不涉及互斥锁定。

听起来像是:

  • 矩阵写入发生-在发布()之前(规则1)
  • 发布()发生-在syncChanges()之前(规则2)
  • syncChanges()发生-在读矩阵之前(规则1)

因此,代码确实为矩阵建立了一个发生前链。

但我不相信。并发性很难,我不是领域专家。我错过了什么?这真的安全吗?

EN

回答 4

Stack Overflow用户

发布于 2013-02-05 19:22:31

就可见性而言,您所需要的只是在任何易失性字段上进行易失性读写操作.这是可行的

代码语言:javascript
复制
final    float[][] matrix  = ...;
volatile float[][] matrixV = matrix;

螺纹1

代码语言:javascript
复制
matrix[x][y] = n;
matrixV = matrix; // volatile write

螺纹2

代码语言:javascript
复制
float[][] m = matrixV;  // volatile read
myVar = m[x][y];

or simply
myVar = matrixV[x][y];

但这只适合于更新一个变量。如果写入线程正在写入多个变量,而读取线程正在读取它们,则读取器可能会看到不一致的图片。通常由读写锁来处理。抄写可能适合于某些使用模式。

Doug有一个新的"StampedLock“http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166edocs/jsr166e/StampedLock.html for Java8,它是读写锁的一个版本,对于读锁来说要便宜得多。但使用起来也难得多。基本上,读取器获得当前版本,然后继续读取大量变量,然后再次检查版本;如果版本没有更改,则在读取会话期间没有并发写入。

票数 4
EN

Stack Overflow用户

发布于 2013-02-05 19:53:29

这对于发布矩阵的单个更新确实是安全的,但它当然不提供任何原子性。这是否可以取决于您的应用程序,但它可能应该记录在这样的实用程序类中。

但是,它包含了一些冗余,可以通过设置sync字段final来进行改进。该字段的volatile访问是两个内存屏障中的第一个;根据约定,调用incrementAndGet()对内存的影响与对易失性变量的写入和读取相同,调用get()与读取的效果相同。

因此,代码可以依赖于这些方法单独提供的同步,并使字段本身成为final

票数 4
EN

Stack Overflow用户

发布于 2013-02-05 19:29:05

使用volatile不是同步所有事情的灵丹妙药。可以保证,如果另一个线程读取易失性变量的更新值,它们也会在此之前看到对非易失性变量所做的每一项更改。但是,没有保证其他线程会读取更新的值

在示例代码中,如果对matrix进行多次写入,然后调用publish(),而其他线程调用synch(),然后读取矩阵,则其他线程可能会看到一些、全部或没有更改:

  • 如果从publish()读取更新的值,则所有更改都会发生。
  • 如果读取旧的已发布值,则所有更改都不会泄漏。
  • 如果它读取先前发布的值,则会发生一些更改,但其中一些更改已通过

请参阅这篇文章

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14714847

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档