【问题标题】:Using Synchronized with Thread-Safe Collection?使用与线程安全集合同步?
【发布时间】:2016-02-05 17:25:55
【问题描述】:

假设我有以下代码:

    private ConcurrentHashMap<Integer, Book> shelf;

    public Library(ConcurrentHashMap<Integer, Book> shelf){         
        this.shelf = new ConcurrentHashMap<Integer, Book>(shelf);       
    }

鉴于我使用的是线程安全集合,以下方法可以使用还是我需要担心线程安全?

    public void addBook(int index, Book add){
        shelf.put(index, add);
    }

如果上述方法使用起来不安全,添加同步是否是正确的方法?像这样,

public synchronized void addBook(int index, Book add){
    shelf.put(index, add);
}

【问题讨论】:

  • 你不需要担心他们AFAIK的线程安全
  • @Leo,只要线程之间有数据共享,您就需要担心线程安全。使用线程安全的类意味着您不必担心该类的任何 single 实例的内部一致性,但如果您的代码依赖于 between 两个实例的一致性或更多对象,那么你的代码将需要实现某种同步来保证一致性。每个单独的对象内部一致的事实不足以解决您的问题。

标签: java multithreading


【解决方案1】:

如果您只打电话给shelf.put,您不必担心。因为put 已经是线程安全的,所以你没问题。

当您执行多个一起需要原子的操作时,您需要担心同步。例如,也许你有一个名为 updateBook 的方法,看起来像

public void updateBook(int index, String newTitle){
    Book book = shelf.get(index);
    // do something with book or maybe update book.setTitle(newTitle);
    shelf.put(index, book);
}

这个方法必须是synchronized,否则另一个线程可以得到一本尚未更新的书。

【讨论】:

  • 您通常不想将同步与并发数据结构一起使用。理想情况下,您应该使用这些结构提供的“额外”原子方法来避免全局锁(这首先是数据结构的要点)。
  • 是的,通常不会。通常是关键字。不过,只是想说明一下在此处进行原子操作。当同步队列和阻塞队列一起使用时,我见过死锁。我认为这个答案可能太多了。
【解决方案2】:

synchronized 关键字实质上在整个 addBook 方法周围放置了一个互斥锁。

ConcurrentHashMap 确保所有操作(例如 put)都是线程安全的,但是结合使用检索操作(例如 get)可能会导致您遇到同时从 Hashmap 检索内容的情况你正在投入,并得到意想不到的结果。

单独地,ConcurrentHashMap 中的所有方法都是线程安全的,但是在单独的线程中结合使用,您不一定能确定它们的执行顺序。 (感谢@jtahlborn 的澄清)。

因此,在您的具体情况下,将 synchronized 关键字添加到 addBook 方法是多余的。

如果您正在执行涉及多个检索和放置的更复杂的操作,您可能需要考虑一些无关的锁定(您自己的互斥锁)。

见:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html

【讨论】:

  • 如果我想添加一个检索特定书籍的方法,那么我必须使用 synchronized 关键字和该方法是否正确?
  • @AnnabelleRosemond 这可能是也可能不是必要的。这取决于您的数据在每次检索时需要有多准确。例如,如果您执行 3 个puts 和 1 个get,取决于哪个线程正在调用get,您可能会看到 0-3 个结果。为了确保一切同步,您需要使用相同的锁锁定putget 方法,但这可能会导致您失去使用线程的潜在性能收益。这是准确性和性能的权衡。
  • 如果 get 操作发生在 put 之后,那么对象以及之前发生的所有其他内存更改将是可见的。
  • 这个答案不准确。 ConcurrentHashMap 的 get() 方法当然是线程安全的!
  • 首先,这些仍然是“线程安全的”。您可能无法确定哪个会先发生,但这并不特定于 ConcurrentHashMap。任何使用并发都是如此。
猜你喜欢
  • 1970-01-01
  • 2012-11-12
  • 2013-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-10
相关资源
最近更新 更多