【问题标题】:Fastest Way for Java to write mutexes?Java编写互斥锁的最快方法?
【发布时间】:2012-12-29 12:36:57
【问题描述】:

Mutexes 在许多编程语言中很常见,例如C/C++。我在 Java 中想念他们。但是,我可以通过多种方式编写自己的class Mutex

  • Mutex 上使用简单的同步关键字。
  • 使用二进制semaphore
  • 使用原子变量,就像讨论过的here
  • ...?

最快(最佳运行时间)方法是什么?我认为同步是最常见的,但性能呢?

【问题讨论】:

  • 他们确实通过添加synchronized 为您解决了这个问题。
  • 帮自己一个忙,请考虑阅读这本书:Java Concurrency in practice 这是关于 Java 并发的书。如果你觉得没必要看这本书,请预测以下代码sn-p的结果:What will this do...提示不止一种可能...提示不止两种可能.. .所有这些都是有效的......要理解为什么......阅读这本书

标签: java multithreading performance mutex


【解决方案1】:

互斥锁在许多编程语言中很常见,例如C/C++。我在 Java 中想念它们。

不确定我是否关注你(尤其是因为你在问题中给出了答案)。

public class SomeClass {
    private final Object mutex = new Object();

    public void someMethodThatNeedsAMutex() {
        synchronized(mutex) {
            //here you hold the mutex
        }
    }
}

或者,您可以简单地使整个方法同步,相当于使用this作为互斥对象:

public class SomeClass {

    public synchronized void someMethodThatNeedsAMutex() {
        //here you hold the mutex
    }
}

最快(最佳运行时)方式是什么?

获取/释放监视器本身不会是一个重大的性能问题(您可以阅读this blog post 以查看影响分析)。但是,如果您有许多线程争夺锁,则会产生争用并降低性能。

在这种情况下,如果您主要读取数据,最好的策略是通过使用“无锁”算法不使用互斥锁(正如 Marko 在 cmets 中指出的那样,无锁使用 CAS 操作,这可能涉及重试如果您有很多写入线程,则写入多次,最终导致性能下降)甚至更好,避免跨线程共享太多内容。

【讨论】:

  • 这真的是最快的方法吗?
  • @Johannes 我已经添加了一些信息,但是您需要定义“最快”。
  • 无锁代码最适合读取繁重的场景;由于重试次数过多,如果没有锁,写入繁重的场景实际上会变得更糟。
  • @MarkoTopolnik 在我的情况下,通常是同时读取和写入(两者的数量大致相同)到数据结构中。
  • @assylias 在我的情况下,没有办法让算法无锁。我真的需要互斥锁,而且它们应该尽可能快。
【解决方案2】:

情况正好相反:Java 设计者解决了它很好,你甚至不认识它:你不需要一流的 Mutex 对象,只需要 synchronized修饰符。

如果您想以非嵌套方式处理互斥体的特殊情况,ReentrantLockjava.util.concurrent 提供了丰富的同步工具,远远超出了原始互斥体。

【讨论】:

  • 谢谢,但是同步最快吗?
  • 正如我在 asyliass 的回答下面评论的那样,这取决于
【解决方案3】:

在 Java 中,每个对象都可以用作互斥体。
这些对象通常被命名为“lock”或“mutex”。

您可以为自己创建首选变体的对象,因为它避免了对该锁的外部访问:

  // usually a field in the class
    private Object mutex = new Object();

    // later in methods
    synchronized(mutex) {
      // mutual exclusive section for all that uses synchronized
      // ob this  mutex object

    }

更快的是避免互斥锁,通过思考如果另一个线程读取非实际值会发生什么。在某些情况下,这会产生错误的计算结果,而在其他情况下只会产生最小的延迟。 (但比同步更快)

书中有详细解释

Java并发实践

.

【讨论】:

    【解决方案4】:

    最快(最佳运行时间)的方式是什么?

    这取决于很多事情。例如,ReentrantLock 过去在争用下的性能比使用 synchronized 更好,但是当发布了优化 synchronized 锁定的新 HotSpot 版本时,情况发生了变化。因此,任何一种锁定方式都没有任何固有的东西会偏爱一种互斥锁而不是另一种(从性能的角度来看)——事实上,“最佳”解决方案可以随着您正在处理的数据和您正在使用的机器而改变继续运行。

    另外,为什么 Java 的发明者没有为我解决这个问题?

    他们做到了 - 在几个方面:synchronizedLocks、原子变量,以及java.util.concurrent 中的一大堆其他实用程序。

    【讨论】:

      【解决方案5】:

      您可以运行每个变体的微基准测试,例如原子、同步、锁定。正如其他人所指出的,这在很大程度上取决于机器和使用的线程数。在我自己递增长整数的实验中,我发现在 Xeon W3520 上只有一个线程,同步胜过原子:原子/同步/锁定:8.4/6.2/21.8,每次递增操作的纳秒数。 这当然是一个边界案例,因为从来没有任何争论。当然,在那种情况下,我们也可以看看非同步的单线程长增量,它比原子快六倍。
      使用 4 个线程,我得到 21.8/40.2/57.3。请注意,这些都是所有线程的增量,因此我们实际上看到了减速。对于 64 线程的锁,它会更好一些:22.2/45.1/45.9。
      在使用 Xeon E7-4820 的 4 路/64T 机器上进行的另一项测试产生 1 个线程:9.1/7.8/29.1、4 个线程:18.2/29.1/55.2 和 64 个线程:53.7/402/420。
      还有一个数据点,这次是双 Xeon X5560,1T:6.6/5.8/17.8,4T:29.7/81.5/121,64T:31.2/73.4/71.6。
      因此,在多插槽机器上,存在沉重的缓存一致性税。

      【讨论】:

        【解决方案6】:

        您可以像使用互斥锁或 java.util.concurrent.Semaphore 一样使用 java.util.concurrent.locks.Lock。但是使用 synchronized-keyword 是一种更好的方法:-)

        问候 安德烈

        【讨论】:

          猜你喜欢
          • 2014-12-04
          • 2023-03-23
          • 2010-12-08
          • 2018-05-23
          • 1970-01-01
          • 2014-05-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多