简体   繁体   English

为什么java并发集合真的是线程安全的

[英]Why java concurrent collections are really thread-safe

I was looking at the code of java concurrent collections and I see that they just wrap simple collections with locking some lock in the beginning of the operation and unlocking it in the end. 我正在查看java并发集合的代码,我看到它们只是在操作开始时锁定一些锁定并在最后解锁它的简单集合。

What about volatile ? volatile怎么样? If the back end collection is not volatile the changes could be missed by other threads, and so the thread-saving is somewhat broken. 如果后端集合不是易失性的,那么其他线程可能会错过更改,因此节省了一些线程。 I know that synchronized can solve this issue, but they use just locks without any further synchronization. 我知道synchronized可以解决这个问题,但它们只使用锁而没有任何进一步的同步。

Is that a problem, or am I missing something? 这是一个问题,还是我错过了什么?


Update: 更新:

After a slight discussion I want to rephrase the question a bit. 经过一番讨论,我想稍微改一下这个问题。

I want to use a java collections in a multi threaded environment. 我想在多线程环境中使用java集合。 (For instance currently I'm talking about PriorityBlockingQueue ) (例如,目前我在谈论PriorityBlockingQueue

I want to be sure that the changes one thread makes to the collection (push/pop) are immediately visible to others. 我想确保一个线程对集合(push / pop)所做的更改立即被其他人看到。

It is good that java concurrent collections prevent me from diving into troubles to keep the inner state of the collection stable when number of threads updates it, but I want to be sure that the data itself is visible to all threads. 当线程数量更新时,java并发集合阻止我陷入麻烦以保持集合的内部状态稳定是好的,但我想确保数据本身对所有线程都可见。

The question is: am I correct that java concurrent collections don't provide this feature out of the box? 问题是:我是否正确java并发集合不提供开箱即用的功能? And if I do, what additional (minimalistic cost) techniques should I use in order to do provide the desired visibility? 如果我这样做,我应该使用哪些额外的(简约成本)技术来提供所需的可见性?

Thanks. 谢谢。

From BlockingQueue 's Javadoc: 来自BlockingQueue的Javadoc:

Memory consistency effects: As with other concurrent collections, actions in a thread prior to placing an object into a BlockingQueue happen-before actions subsequent to the access or removal of that element from the BlockingQueue in another thread. 内存一致性效果:与其他并发集合一样,在将对象放入BlockingQueue之前,线程中的操作发生在从另一个线程中的BlockingQueue访问或删除该元素之后的操作之前

PriorityBlockingQueue provides this behaviour by means of a ReentrantLock which (being an implementation of Lock ): PriorityBlockingQueue通过ReentrantLock (作为Lock的实现)提供此行为:

...provide[s] the same memory synchronization semantics as provided by the built-in monitor lock, as described in the JLS ... ...提供[s]与内置监视器锁提供的相同的内存同步语义,如JLS中所述......

yes, you are missing something. 是的,你错过了什么。 the ReentrantLock class provides the same guarantees as synchronized. ReentrantLock类提供与synchronized相同的保证。 And, ReentrantLock and synchronized both provide the same memory guarantees as volatile. 并且,ReentrantLock和synchronized都提供与volatile相同的内存保证。

I was looking at the code of java concurrent collections and I see that they just wrap simple collections with locking some lock in the beginning of the operation and unlocking it in the end. 我正在查看java并发集合的代码,我看到它们只是在操作开始时锁定一些锁定并在最后解锁它的简单集合。

What source were you reading? 你在读什么资料?

This is an over-generalization. 这是一种过度概括。 It depends entirely on which collection you're looking at. 这完全取决于您正在查看的集合。 For example, CopyOnWriteArrayList does nothing of the sort, but creates an entirely new array every time you add or remove elements, which means any open Iterator or ListIterator will continue running with the old data; 例如, CopyOnWriteArrayList不执行CopyOnWriteArrayList操作,但每次添加或删除元素时都会创建一个全新的数组,这意味着任何打开的IteratorListIterator都将继续使用旧数据运行; this effect is intentional. 这种效果是故意的。

What about volatile? 挥发性怎么样? If the back end collection is not volatile the changes could be missed by other threads, and so the thread-saving is somewhat broken. 如果后端集合不是易失性的,那么其他线程可能会错过更改,因此节省了一些线程。 I know that synchronized can solve this issue, but they use just locks without any further synchronization. 我知道synchronized可以解决这个问题,但它们只使用锁而没有任何进一步的同步。

Most of the concurrent collections exist to make sure Iterators continue to operate on the old version rather than the new version that has updated data; 大多数并发集合的存在是为了确保迭代器继续在旧版本上运行,而不是在更新数据的新版本上运行; something that volatile would not guarantee. volatile东西不能保证。 This behavior also means that the Iterators will not be left in an inconsistent state, and thus preventing ConcurrentModificationException from being thrown. 此行为还意味着迭代器不会处于不一致状态,从而阻止抛出ConcurrentModificationException

EDIT : since the original question was clarified, this answer is no longer relevant. 编辑 :由于原来的问题得到澄清,这个答案已经不再适用了。 The answer below is relevant for the case when using Collections.synchronized* methods for making non-threadsafe collections threadsafe. 下面的答案与使用Collections.synchronized *方法使非线程安全集合线程安全的情况相关。


If you synchronize a block of code that will also cause different threads to synchronize the (possibly changed) state. 如果同步一个代码块,它也会导致不同的线程同步(可能已更改)状态。

[...] Either solution requires the clkID variable to be reconciled with main memory. [...]两种解决方案都要求clkID变量与主内存协调。 Accessing the clkID variable from synchronized method or block does not allow that code to execute concurrently, but it does guarantee that the clkID variable in main memory is updated appropriately. 从synchronized方法或块访问clkID变量不允许该代码并发执行,但它确保主存储器中的clkID变量得到适当更新。 Main memory is updated when the object lock is obtained before the protected code executes, and then when the lock is released after the protected code executes. 在受保护代码执行之前获取对象锁定时更新主存储器,然后在受保护代码执行之后释放锁定时更新主存储器。 [...] [...]

Source : Use Synchronized or Volatile when Accessing Shared Variables 来源访问共享变量时使用同步或易失性

Here is the point wise answers to your questions. 以下是您的问题的明智答案。 1. All Java collection data structures in concurrency package do not wrap corresponding non thread safe collection data structures. 1.并发包中的所有Java集合数据结构都不包装相应的非线程安全集合数据结构。 2. There are ways to achieve thread safety without using locks or synchronization. 2.有一些方法可以在不使用锁或同步的情况下实现线程安全。 Writing lock free data structures is more involved and normally you should avoid for general development. 编写无锁数据结构更为复杂,通常应避免进行一般开发。 But if there is one available, you should use them instead of the corresponding lock version of the data structure. 但是如果有一个可用,则应该使用它们而不是数据结构的相应锁定版本。 3. Lock free data structure do not use locks, still provide all the safety and liveness property. 3.无锁数据结构不使用锁,仍提供所有安全性和活跃性。 4. Sample API is ConcurrentSkipListMap, which is lock free concurrent probabilistic map ( probably the most complicated implementation in Java) 4.示例API是ConcurrentSkipListMap,它是无锁并发概率图(可能是Java中最复杂的实现)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM