【问题标题】:syncronize instance instead of of volatile primitives同步实例而不是 volatile 原语
【发布时间】:2017-08-03 19:16:56
【问题描述】:

我有一个对象:

class Data{
   int a;
   int b;
   AtomicIntegerArray c = new AtomicIntegerArray(10000);   
}

线程 A 和 B 在以下场景中使用此对象:

A 创建一个数据(现在称为“数据”)

A 将数据发送到队列。

B 从队列中读取,获取数据。

B 更新数据的字段并向 A 发出数据已被处理的信号。

B 睡着了。

A 开始处理数据的字段。

现在,到目前为止,我为确保内存可见性所做的工作是:

 class Data{
   volatile int a;
   volatile int b;
   volatile AtomicIntegerArray c = new AtomicIntegerArray(10000);   
}

这可行,但让我担心。当线程A取回数据时,它只需要在开始时同步一次内存,而不是每次接触到一个字段。我想我可以通过简单地做一个来实现这一点:

synchronized(data){}

一旦它知道数据已经更新,因此使用 Data 的第一个实现。这样我只做一次昂贵的内存同步。

我说的对吗?我是否还需要确保线程 B 在将数据“移交”给线程 A 之前同步数据?

请记住,我只对内存同步/可见性感兴趣,没有锁定机制和线程之间的信号是无关紧要的。我已经涵盖了。

信号:

class A implements callback{
    private volatile boolean dataProcessed;
    private final Data data = new Data();

    @Override
    public void dataHasBeenProcessed(){
         dataProcessed = true;
    } 

    void someMethod(){
       dataProcessed = false;
       threadB.processData(data, this);
       while(!dataProcessed)
          ...sleep;
       data.workOnFields();

}

因此,A 将数据发送给 B,然后在处理数据时轮询 B 在回调方法中设置的 volatile 布尔值。

【问题讨论】:

  • > B 更新数据的字段并向 A 发出数据已被处理的信号。 B如何向A发送信号?你能粘贴那个代码吗?
  • 当然,我已经更新了
  • 作为答案发布。太大了,不适合评论:)

标签: java multithreading memory-management thread-safety


【解决方案1】:

TL;DR您根本不需要volatilesynchronized

线程 A 和 B 绝不会同时接触对象,因此只要它们之间的切换建立 happens-before 边界,它们将始终看到最新的数据。

例如,如果队列是BlockingQueue,你会得到这个保证:

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

所以只要队列是BlockingQueue,而不是通用的Queue,从线程A 到线程B 的切换是安全的。

如果从线程 B 返回到线程 A 的信号正在使用例如CountDownLatch,你得到这个保证:

内存一致性效果:在计数达到零之前,线程中的操作在调用countDown() 之前happen-before 操作在从另一个线程中的相应await() 成功返回之后。 p>

所以从线程 B 切换回线程 A 也是安全的。

总结:

  • 线程 A发送对象之前所做的一切发生在线程 B 接收对象之前。

  • 线程 B发送信号之前所做的一切发生在线程 A 接收到信号之前。

结论:

不需要volatilesynchronized

【讨论】:

  • 这是个好消息。不幸的是,我没有建立任何我所知道的发生之前的边界。两个线程通过分配易失性指针进行通信。例如,当 B 完成数据时,它会为数据的指针分配一个空指针,然后 A 在 B 执行期间轮询该指针。我的队列是另一回事,但它不是阻塞队列。所以让我们玩弄我什么都没有建立的想法。那你有什么想说的?
【解决方案2】:

我说的对吗?我是否还需要确保线程 B 在将数据“移交”给线程 A 之前同步数据?

既然您已经使用volatile 变量建立了happens-before,那么您是正确的。如果线程 B 写入 volatile 变量 dataProcessed,它会将其所有内存更改(至 Data)提供给读取相同 volatile 变量的线程 A。

既然你问到内存可见性,可以这样想:当线程 B 写入 volatile 变量时,它保证 flush 它改变的所有变量(Data)到主记忆。 (从运行线程 B 的 CPU 内核)。线程 A 现在可以看到这一切。这是happens-before关系的本质。

虽然可行,但这不是一个好的设计。您有一个由多个线程共享的对象 (Data)。这个对象应该是线程安全的。假设有人在将来添加了一个线程 C 并忘记强制执行之前发生的关系。这将引入难以发现的同步错误,这些错误通常需要很长时间才能识别,因为它们会间歇性或很少发生。

【讨论】:

  • 谢谢!我知道设计问题,但我的目标是在产生 0 个垃圾的情况下进行良好的通信。我虽然 volatile 只确保 volatile 标记的变量在写入时被刷新,a.k.a 不是陈旧的,不是所有的线程缓存变量。但似乎是这样。
猜你喜欢
  • 2018-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-30
  • 2023-02-07
相关资源
最近更新 更多