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

jut*_*tky 8 java collections concurrency multithreading thread-safety

我正在查看java并发集合的代码,我看到它们只是在操作开始时锁定一些锁定并在最后解锁它的简单集合.

怎么样volatile?如果后端集合不是易失性的,那么其他线程可能会错过更改,因此节省了一些线程.我知道synchronized可以解决这个问题,但他们只使用锁而没有任何进一步的同步.

这是一个问题,还是我错过了什么?


更新:

经过一番讨论,我想稍微改一下这个问题.

我想在多线程环境中使用java集合.(例如我目前正在谈论的PriorityBlockingQueue)

我想确保一个线程对集合(push/pop)所做的更改立即被其他人看到.

当线程数量更新时,java并发集合阻止我陷入麻烦以保持集合的内部状态稳定是好的,但我想确保数据本身对所有线程都可见.

问题是:我是否正确java并发集合不提供开箱即用的功能?如果我这样做,我应该使用哪些额外的(简约成本)技术来提供所需的可见性?

谢谢.

Sim*_*onJ 11

来自BlockingQueue的Javadoc:

内存一致性效果:与其他并发集合一样,在将对象置于从另一个线程中访问或删除该元素之后的BlockingQueue 发生之前的操作之前BlockingQueue,线程中的操作.

PriorityBlockingQueue通过a ReentrantLock(作为实现Lock)提供此行为:

...提供[s]与内置监视器锁提供的相同的内存同步语义,如JLS中所述......


小智 8

是的,你错过了什么.ReentrantLock类提供与synchronized相同的保证.并且,ReentrantLock和synchronized都提供与volatile相同的内存保证.

  • "所有`Lock`实现**必须执行由所提供的相同的存储器同步语义内置监视器锁定,如在JLS描述的" - 从http://download.oracle.com/javase/6/docs/api /java/util/concurrent/locks/Lock.html (3认同)
  • 是的,您可以在一个函数中获取锁定并将其释放到另一个函数中 - 但建议的用法是在获取锁定后立即在finally块中调用`unlock()`.否则,编程以确保最终调用它. (2认同)

Pow*_*ord 6

我正在查看java并发集合的代码,我看到它们只是在操作开始时锁定一些锁定并在最后解锁它的简单集合.

你在读什么资料?

这是一种过度概括.这完全取决于您正在查看的集合.例如,CopyOnWriteArrayList什么都不做,但每次添加或删除元素时都会创建一个全新的数组,这意味着任何打开IteratorListIterator将继续运行旧数据; 这种效果是故意的.

挥发性怎么样?如果后端集合不是易失性的,那么其他线程可能会错过更改,因此节省了一些线程.我知道synchronized可以解决这个问题,但它们只使用锁而没有任何进一步的同步.

大多数并发集合的存在是为了确保迭代器继续在旧版本上运行,而不是在已更新数据的新版本上运行; 一些volatile不能保证的东西.此行为还意味着迭代器不会处于不一致状态,从而防止ConcurrentModificationException被抛出.


Nee*_*aks 5

编辑:由于原来的问题得到澄清,这个答案已经不再适用了.下面的答案与使用Collections.synchronized*方法使非线程安全集合线程安全的情况相关.


如果同步一个代码块,它也会导致不同的线程同步(可能已更改)状态.

[...]两种解决方案都要求clkID变量与主存储器协调.clkID从同步方法或块访问变量不允许该代码同时执行,但它确保clkID主存储器中的变量得到适当更新.在受保护代码执行之前获取对象锁定时更新主存储器,然后在受保护代码执行之后释放锁定时更新主存储器.[...]

来源:访问共享变量时使用同步或易失性