2014-01-14 29 views
0

我正在实现一个程序,其中主线程将各种消息推送到工作线程,并且工作线程将工作结果推回到主线程。BlockingQueue在任何时候都会与所有线程相同吗?

为此,我计划使用两个队列,一个推到工作者线程,另一个从队列中拉出。

据我所知,线程缓存对象,因此如果它们将一个对象指向另一个线程可能的队列poll(),则此更改可能不会立即可见,即可能不会同步。它是否正确?

但是,如果我要使用BlockingQueue,则此队列上的所有操作都应该立即对所有线程可见?

+0

'BLockngQueue'位于'Java.util.concurrent'包中。整个程序包用于并发(线程安全)编程。任何实现该接口的东西都是为了你想要的。 –

+0

@BrianRoach可能会也可能不会回答我的问题......我读了类的文档,但它提到了它只是阻塞,直到另一个线程准备从它读取,而不是它会导致线程刷新其缓存。 – corazza

+1

@yannbane再次忽略,整个包是用于并发编程的,安全发布(线程缓存)涉及给定类中的对象的引用*,而不一定是它的内容。一个'BlockingQueue'完全是线程安全的,但它在你所说的内容上的可见性取决于包含的类是正确发布它(通过'final'变量或'volatile'变量)还是仅仅依靠发生 - 行为之前。 –

回答

1

这个答案假定您正在讨论线程安全的数据结构或构造。如果您使用常规的非线程安全字段,它不能保证它可以正常工作,但也不能保证它不起作用。注意:这是针对线程安全的单元测试有用的,但不是简单的证明。

此更改可能不会立即显示,即它可能不会同步。它是否正确?

是。延迟时间通常在50 - 100纳秒左右,因此可能几乎是立即的。

这个队列上的所有操作都应该立即对所有线程可见?

这在真机中是不可能的。你可以说的是,你应该永远不会因为可见性问题而得到一个错误,其中大部分是非常轻微的延迟。 (但是因为它产生的垃圾,你可以得到在最坏的情况下,全GC的延迟,如果机器没有得到在这种情况下,延迟可能是几小时或几天休眠)

+0

如果线程A和B具有对同一对象的引用,并且A更改了该对象,那么此更改将保证最终在线程B中可见?我认为线程可以更长时间地保存对象的缓存,例如,如果你想在它们之间共享一个对象的变量,但仍然保证这两个线程都可以看到变化,你必须使用'volatile'关键词。 – corazza

+0

正确,并且保证您不会看到不一致的更新。这是更重要的功能。这个改变对于两个线程都是可见的,但是在CPU中不会立即发生。最小时钟周期为〜0.3 ns,如果设置了一个易失性变量并在另一个线程中读取它,则至少需要70个时钟周期。如果你写入一个volatile并在* same *线程中读取,它将需要约5 ns。对于像分配对象的队列这样的复杂数据结构,这需要50 - 100 ns的量级。 –

+0

所有其他答案/评论都会显示*“如果线程A和B对同一个对象有引用,并且A更改了该对象,那么这个更改将保证最终在线程B中可见?”* - is ** false **, 不正确!例如。它与你提到的延迟无关,而是与* Java *中的线程一起实现。我认为你回答了有关线程的问题*一般* ... – corazza

1

javadocBlockingQueue接口这样说:

BlockingQueue”实现是线程安全的,所有排队方法都是通过内部锁或其他形式的并发控制自动实现其效果。

你因此保证改变一个线程队列做出“立即” ......对其他线程可见的内存模型的意义。照顾到putpoll呼叫的同步。 (任何缓存效果都由javadoc中提到的机制来处理。)

当然,这并不意味着它们都会在语义级别看到更改。这取决于诸如线程调度之类的事情。 (如果一个对象被添加到一个队列中,只有一个线程可以删除它,如果它在第二个线程看到队列状态之前这么做了......无论出于何种原因......第二个线程不会注意到该条目已被添加,然后被删除。)

“立即”的粒度也未指定。例如,如果JVM只使用一个CPU /内核,则线程只能在调用put调用的线程被解除调度之后从poll返回。另外,缓存内存缓存中的变化需要花费大量的时间。


1 - 假定实现类满足“契约” ......但如果没有它是越野车。

+0

“任何缓存效果都由javadoc中提到的机制来处理” - 如果你在回答器中包含了一些解释 – corazza

+1

@yannbane - 您想要我解释Java内存模型吗?没有可以做!但是我可以推荐你买一本“Java Concurrency in Action:Goetz et al ...的副本,并阅读最后一章。 –

+0

谢谢我会检查出来,+1 – corazza

1

根据你原来的问题和评论,你混淆了两个不同的东西。

任何实现BlockingQueue的线程安全并正确发布。实现该接口的类的内部各不相同,因此声称“此队列上的所有操作都应该立即对所有线程可见”可能不适用于直接的某些定义。 “非常快”将是一个更加贴切的描述;它可能没有使用阻塞/锁定机制,并且总是涉及线程调度。

安全发布(线程缓存)与线程在Java中的工作方式有关,是一个更大的话题。必须安全地发布对该BlockingQueue的引用,以供所有线程查看。在大多数情况下,当使用这样的结构时,你可能做的是正确的事情。也就是说,要么将参考文献传递给Runnable构造函数,要么将其创建为final变量。但是,真的,只要你没有得到一个NullPointerException ...线程可以看到它。

当您将对象传递给多个线程之后,在对象内部实例化某个对象出现安全发布问题。

该主题事实上的参考文献是Java Concurrency in Practice,非常值得拥有。

+0

好的,这听起来不错...但是我我担心我在队列中推送的对象不会被同步......我可能是错的,但是如果线程B调用的run方法创建一个对象并将其放入队列中,那么当前正在运行的对象线程A有一个引用,这个对象是否会在线程之间同步?就像它的引用一样?例如,'run'创建一个名为'text'的单个公共字段的'Message',并将该字段设置为一个消息“',并将其推送到队列中(所有这些都在同步块中),那个对象是否与其他线程相同? – corazza

+1

好吧,这是在讨论安全发布。你保证A会看到所有东西这是在它从队列中获得引用之前发生的。可见性进入onc e这两个线程都有对该对象的引用,然后你修改该对象。不能保证在A中看到通过B中某种同步机制完成的对象任何更改(反之亦然)。例如,如果在A具有对象的引用之后,您要在B中为“text”分配不同的'String'。例如,将该字段设置为“挥发性”,可以保证A看到了变化。 –

相关问题