【问题标题】:How to initialize a hashTable with safe-thread object as value?如何以安全线程对象为值初始化哈希表?
【发布时间】:2018-04-05 14:03:59
【问题描述】:

HashTable 是一个线程安全的集合,但使用 ArrayList(不是线程安全的)作为值初始化它会危及整个线程安全方面吗?

 Hashtable <Employee, ArrayList<Car>> carDealership = new Hashtable<>();

此外,我计划将 ArrayLists 的每个操作包装在 synchronized 块中,以防止在使用任何方法时出现任何竞争条件。

但我还没有将 HashTable 中的 ArrayLists 声明为同步列表,这是通过以下代码实现的

 Collections.synchronizedList(new ArrayList<>())

当我将 ArrayLists 添加到 HashTable 时,就会发生这种情况。

如何确定HashTable 中的ArrayLists 是线程安全的?

将线程安全的ArrayList 传递给hashTableput() 方法就足够了吗?(甚至不用担心HashTable?) 因此 HashTable 的 put() 方法甚至无法识别我是否正在传递线程安全/不安全参数?

注意:线程安全是一项要求。否则我不会选择这个实现。

【问题讨论】:

  • 旁注:Hashtable 是线程安全的,但您不使用ConcurrentHashMap 是否有特定原因?它提供了更强大的 API。
  • @Hulk 主要是因为 HashTable 有一个快速失败的迭代器,我也想测试一下。使用 ConccurentHashMap 仍然无法解决我必须将 List 作为值传递的问题。
  • 是的,好的,ConcurrentHashMap 上的迭代会迭代快照,并且不会因ConcurrentModificationException 而失败。我并不是说使用ConcurrentHashMap 可以解决你的问题,我只是好奇你为什么考虑使用古老的Hashtable
  • ConcurrentHashMapHashtable 中替换一个列表(被声明为同步的)确实会引发一个不安全的线程问题,如下面的答案所述?
  • @Hulk 正如我所说,我计划将 ArrayLists 的每个操作包装在一个同步块中,以防止在使用任何方法操作时出现任何竞争条件。现在回到问题的答案,发帖人承认 HashTable 实际上可能会使引用处于无效状态。毕竟我的解决方案是线程安全的吗?

标签: java multithreading arraylist thread-safety hashtable


【解决方案1】:

确保HashtableConcurrentHashMap 中的值是线程安全的唯一方法是以防止任何人添加您无法控制的内容的方式包装它。切勿将Map 本身或其中包含的任何Lists 暴露给代码的其他部分。如果需要,请提供获取快照副本的方法,提供向列表添加值的方法,但请确保包装地图的类将创建所有可以添加到其中的列表。迭代地图中的“实时”列表需要外部同步(如 in the JavaDocs of synchronizedList 所述)。

HashtableConcurrentHashMap 都是线程安全的,因为并发操作不会使它们处于无效状态。这意味着例如如果您从具有相同键的两个线程调用put,其中一个将返回值,另一个插入为“旧”值。但是,如果没有外部同步,您当然无法提前知道哪个是第一个,哪个是第二个。

不过,实现是完全不同的:HashtableCollections.synchronizedMap(new HashMap()); 返回的同步 Map 相似,因为它们基本上都在大多数方法中添加了 synchronized 修饰符。如果您有很多线程(即锁的高争用)主要读取但只是偶尔修改映射,这可能会效率低下。 ConcurrentHashMap 提供更细粒度的锁定:

检索操作(包括get)一般不会阻塞

这可以产生明显更好的性能,具体取决于您的用例。我还提供了更丰富的 API,具有强大的搜索和批量修改操作。

【讨论】:

    【解决方案2】:

    是的,在这种情况下使用ArrayList 不是线程安全的。您始终可以从表中获取对象并对其进行操作。

    CopyOnWriteArrayList 是它的一个很好的替代品。

    但是您仍然遇到这种情况,当一个线程获取(保存在变量中)集合,而另一个线程用另一个线程替换。
    如果您不打算替换表格内的列表,那么这不是问题。

    【讨论】:

    • 不幸的是我也必须操作写操作,这种取消资格CopyOnWriteArrayList。但是,如果不是线程安全的,为什么在这种情况下使用ArrayList?只要我只将同步列表添加到哈希表中,并且对某些列表的所有操作都将包含在synchronized blocks?
    • @CatalinGhita CopyOnWriteArrayList 是线程安全的,使用此列表或同步列表应该没有任何重大区别。我的意思是,如果您要替换某些员工的现有列表(您很可能不会这样做),那么这将创建一个非线程安全的情况。
    • 您能否详细说明为什么替换 List 会产生非线程安全的情况?这与向 HashTable 添加线程安全列表几乎相同
    • @CatalinGhita 想象一个线程将员工“bob”的列表保存在一个变量中以供以后处理。同时,另一个线程将“bob”列表替换为另一个列表。然后,当第一个线程开始处理它保存的列表时,它将在一个可能已过时的列表上运行,对其所做的任何更改都不会反映在表中的列表中。
    • 为什么它会在过时的列表上工作?只要列表本身被标记为同步
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多