【问题标题】:how to judge which object to be synchronized in java thread?java线程中如何判断要同步哪个对象?
【发布时间】:2012-04-08 02:48:19
【问题描述】:

我想我可以使用任何对象作为块进行同步,例如:

synchronized(new Object()){
}

但我经常看到在需要 hashmap 是线程安全的时候同步一个 hashmap。但我认为我可以使用另一个对象来代替 hashmap。那么哪个对象最好同步呢?

【问题讨论】:

    标签: java synchronized


    【解决方案1】:

    synchronized (new Object()) { ... } 一点用都没有,因为没有其他线程会获得被锁定的对象。

    您应该在“保护”资源的对象上进行同步。显然,如果多个线程需要访问同一个资源,则保护该资源的对象需要对两个线程都可用。

    也许你已经看到了:

    class SomeClass {
    
        final private Object lock = new Object();
    
        void method() {
            ...
            synchronized (lock) {
                ...
            }
            ...
        }
    }
    

    然而,这与 synchronized (new Object()) 非常不同,因为在上面的代码中,相同的对象用于执行该方法的所有线程。

    但是当需要 hashmap 是线程安全的时,我经常看到同步一个 hashmap。但我认为我可以使用另一个对象来代替 hashmap。那么哪个对象最好同步呢?

    是的,如果哈希映射是要在多个线程之间共享的资源,那么在该对象上进行同步是很常见的。

    synchronized (someHashMap) {
        ... use someHashMap in a thread safe way ...
    }
    

    是的,您也可以在某些成员字段lock = new Object() 上进行同步。事实上,有时首选使用专用锁对象进行同步,因为它不会干扰您要保护的对象的同步方法。

    【讨论】:

      【解决方案2】:

      每次使用一个新对象进行同步并没有使任何线程更安全。您需要每次重复使用相同的同步对象。

      除此之外,您可以使用另一个“同步对象”而不是您锁定的数据结构(但您需要确保在任何地方都使用相同的引用!;))。


      事实上,许多人在对象中使用专用同步对象(例如private final Object sync = new Object()),因为当另一个线程可以在代码中的其他位置锁定对象时,同步this 可能会很危险。

      【讨论】:

      • 非常感谢您。你是这个意思吗?我可以使用任何对象作为同步对象,但如果我想确保安全,我应该将同步对象保留在其他代码中?
      • 您需要使用 same 锁定机制保护多个线程接触的数据结构。如果您使用需要锁定完全相同的引用的synchronized 块。
      • +1 同步到已知类而不是专用对象时应该小心,除非您确切知道该类的作用。如果该类内置了某种同步,则可能会导致死锁与该类内部与自身同步的代码冲突。
      【解决方案3】:

      首先,做synchronized(new Object())是个坏主意,因为每个线程都会创建自己的对象,所以同步没有意义。

      在您希望同步访问现有对象的示例中,在该对象上进行同步是有意义的。创建其他对象进行同步没有任何好处。

      但是,如果您想做一些更花哨的事情来增加并发性——例如,将映射的可能键分成几组,并用不同的锁保护这些组中的每一个——然后创建单独的对象同步可能很有用。但在您真正确定需要解决的性能问题之前,我不会为此类事情烦恼。

      【讨论】:

      • -1 表示“创建其他要同步的对象没有任何好处。”如果您与现有对象同步,该对象在其自己的方法中与自身同步,您可能会在没有意识到的情况下导致死锁。
      • @ErickRobertson - 嗯?死锁需要在两个不同的对象上获取锁。如果一个线程在对象 A 上获得一个锁,然后在该对象中调用一个同步方法,它已经持有锁,所以没有问题。
      • 在多线程系统中,其他线程也可能与该对象进行交互。有几种方法可能另一个线程可能已经在对象上拥有一个锁,并试图获取第一个线程在尝试与对象同步时已经拥有的锁。
      【解决方案4】:

      似乎没有人提到它,但根据您的要求,首先使用线程安全对象可能可以让您删除所有锁定逻辑。

      HashMap 的情况下,有ConcurrentHashMap,这是一个线程安全版本,带有一些额外的原子操作(putIfAbsent 等)。

      【讨论】:

        【解决方案5】:

        真的没关系,只要你每次都用同一个。我个人更喜欢使用需要同步的实际对象(例如您的HashMap),因为它更清楚地传达了我正在同步它,并且不需要另一个对象实例(可能带有混乱的变量)。

        请注意,如果您实际使用的是:

        synchronized(new Object()) {
            // ...
        }
        

        那么你做错了,因为new Object() 每次都会创建一个新对象;您需要保持相同的引用才能正确同步。

        【讨论】:

          猜你喜欢
          • 2020-11-28
          • 1970-01-01
          • 1970-01-01
          • 2017-10-04
          • 2019-10-03
          • 1970-01-01
          • 1970-01-01
          • 2018-05-22
          • 1970-01-01
          相关资源
          最近更新 更多