我有一个对象:
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获取数据时,它只需要在开始时将其同步一次,而不是每次接触一个字段时。我想我可以通过简单地做A来实现这个目标:
synchronized(data){}
一旦知道数据已经更新,就可以使用Data的第一个实现。这样我只需要进行一次昂贵的内存同步。
我说错了吗?我是否还需要确保线程B在将数据交付之前同步数据"线程A?
请记住,我只对内存同步/可见性感兴趣,没有锁定机制,并且线程之间的信令无关紧要。我有这个。
SIGNALING:
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在回调方法中设置。
答案 0 :(得分:1)
TL; DR 您根本不需要volatile
或synchronized
。
线程A和B从不同时触摸对象,因此只要它们之间的切换建立发生在边界之外,它们将始终看到最新数据。
例如,如果队列是BlockingQueue
,则可以获得此保证:
内存一致性效果:与其他并发集合一样,在将对象放入
BlockingQueue
之前,线程中的操作发生在之后的操作中,该操作是在访问或删除该元素之后执行的另一个帖子中的BlockingQueue
。
因此,只要队列是BlockingQueue
,而不是通用Queue
,从线程A到线程B的切换就是安全的。
如果来自线程B的信号回到线程A正在使用例如一个CountDownLatch
,你得到这个保证:
内存一致性影响:在计数达到零之前,在调用
countDown()
之前的某个线程中的操作发生在之后的操作发生之后,从另一个中的相应await()
成功返回线程。
因此从线程B切换回线程A也是安全的。
<强>要点:强>
线程A在发送对象之前在线程B收到对象之前发生
在发送信号之前,线程B执行之前
<强>结论:强>
不需要volatile
或synchronized
。
答案 1 :(得分:0)
我说错了吗?我是否还需要确保线程B在将数据交付之前同步数据&#34;线程A?
由于您已使用volatile
变量建立发生之前,您是正确的。如果线程B写入 volatile 变量dataProcessed
,它将使线程A可以读取相同的volatile变量的所有内存更改(到Data
)。
由于您询问内存可见性,请以这种方式考虑:当线程B写入volatile变量时,保证刷新它更改的所有变量(Data
)主要记忆。 (从线程B运行的CPU核心)。线程A现在可以看到它。这是发生在以前的关系的本质。
虽然它有效,但这不是一个好的设计。您有一个由多个线程共享的对象(Data
)。该对象应该是线程安全的。假设某人将来添加了一个Thread C并忘记强制执行之前发生的关系。这将导致难以找到通常需要很长时间才能识别的同步错误,因为它们间歇性地或很少发生。