栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

在Java中,我可以依靠原子引用引用来实现写时复制吗?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

在Java中,我可以依靠原子引用引用来实现写时复制吗?

与其尝试推出自己的解决方案,不为何使用ConcurrentHashMap作为您的集合,而只是将所有值设置为某个标准值?(像

Boolean.TRUE
这样的常量会很好用。)

我认为这种实现方式在许多读者很少的情况下效果很好。甚至还有一个构造函数,可让您设置预期的“并发级别”。

更新: Veer建议使用
Collections.newSetFromMap
实用程序方法将ConcurrentHashMap转换为Set。由于该方法令人

Map<E,Boolean>
怀疑,因此它会将所有值设置
Boolean.TRUE
为幕后操作。


更新:解决海报的示例

那可能是我最终要解决的问题,但是我仍然对我的极简解决方案如何失败感到好奇。– MilesHampson

通过一些调整,您的极简解决方案将可以正常工作。我担心的是,尽管现在它很小,但将来可能会变得更加复杂。很难记住在进行线程安全操作时要假设的所有条件,尤其是如果您要在几周/几个月/几年后返回代码以进行看似微不足道的调整时。如果ConcurrentHashMap具有足够的性能来满足您的所有需求,那么为什么不使用它呢?所有令人讨厌的并发详细信息都被封装了起来,甚至从现在开始的6个月,您将很难将其弄乱!

您确实需要至少一项调整才能使当前解决方案生效。正如已经指出的那样,您可能应该将

volatile
修饰符添加到
global
的声明中。我不知道您是否具有C
/ C ++背景,但是当我得知
volatile

Java的语义实际上比C复杂得多时,我感到非常惊讶。如果您打算使用Java进行大量并发编程,那么熟悉Java内存模型的基础是一个好主意。如果您不引用
global
某个
volatile
引用,那么在
global
尝试更新它之前,没有线程会看到该值的任何更改。
synchronized

块将刷新本地缓存并获取更新的参考值。

但是,即使添加了

volatile
,仍然存在 很大的 问题。这是一个有两个线程的问题场景:

  1. 我们从空集或开始
    global={}
    。线程
    A
    B
    两者在其线程本地缓存的内存中均具有此值。
  2. 线程
    A
    获取获得
    synchronized
    锁定
    global
    并通过复制副本
    global
    并将新密钥添加到集合中来开始更新。
  3. 当Thread
    A
    仍在
    synchronized
    块内时,Thread
    B
    将其本地值读取
    global
    到堆栈上并尝试进入该
    synchronized
    块。由于线程
    A
    当前位于监视器的线程
    B
    块内。
  4. 线程
    A
    通过设置参考并退出监视器来完成更新,结果为
    global={1}
  5. 线程
    B
    现在能够进入监视器并制作副本
    global={1}
  6. 线程
    A
    决定进行另一次更新,读取其本地
    global
    引用,然后尝试进入该
    synchronized
    块。 由于线程B当前保持锁定状态,
    {}
    因此没有任何锁定
    {1}
    ,线程
    A
    成功进入监视器!
  7. 线程
    A
    还制作的副本以
    {1}
    进行更新。

现在,线程

A
B
都在
synchronized
块内,并且它们具有相同的副本
global={1}
集。 这意味着他们的更新之一将丢失!
这种情况是由于您正在对存储在
synchronized
块中要更新的引用中的对象进行同步而引起的。您应该始终非常小心地使用要同步的对象。您可以通过添加新变量来充当锁来解决此问题:

private volatile Collection global = new HashSet(); // start threading after thisprivate final Object globalLock = new Object(); // final reference used for synchronizationvoid allUpdatesGoThroughHere(Object exampleOperand) {  // My hypothesis is that this prevents operations in the block being re-ordered  synchronized(globalLock) {    Collection copy = new HashSet(global);    copy.remove(exampleOperand);    // Given my hypothesis, we should have a fully constructed object here. So a     // reader will either get the old or the new Collection, but never an     // inconsistent one.    global = copy;      }}

这个错误非常阴险,其他答案都没有解决。
这些疯狂的并发细节使我建议使用已经调试过的java.util.concurrent库中的某些内容,而不是尝试自己编写一些内容。
我认为上述解决方案会奏效-但是将其再次拧紧有多容易?这样会容易得多:

private final Set<Object> global = Collections.newSetFromMap(new ConcurrentHashMap<Object,Boolean>());

由于是引用,因此

final
您不必担心使用陈旧引用的线程,并且由于
ConcurrentHashMap
内部处理了所有讨厌的内存模型问题,因此您不必担心监视器和内存屏障的所有讨厌的细节!



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/399963.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号