5

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

  • 在 .上使用简单的同步关键字Mutex
  • 使用二进制信号量
  • 使用原子变量,就像这里讨论的那样。
  • ...?

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

4

6 回答 6

15

互斥锁在许多编程语言中非常常见,例如 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
    }
}

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

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

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

于 2013-01-15T12:07:19.597 回答
5

情况正好相反:Java 设计者解决得非常好,以至于您甚至都认不出它:您不需要一流的Mutex对象,只需要synchronized修饰符。

如果你有一个特殊的情况,你想以非嵌套的方式处理你的互斥锁,那么总是有ReentrantLock并且java.util.concurrent提供了一个远远超出原始互斥锁的同步工具的聚宝盆。

于 2013-01-15T12:09:53.227 回答
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并发实践

.

于 2013-01-15T12:21:42.250 回答
2

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

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

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

他们做到了——以多种方式:synchronizedLocks、原子变量,以及java.util.concurrent.

于 2013-01-15T12:19:34.370 回答
1

您可以运行每个变体的微基准测试,例如原子、同步、锁定。正如其他人所指出的,这在很大程度上取决于机器和使用的线程数。在我自己递增长整数的实验中,我发现在 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。
因此,在多插槽机器上,存在沉重的缓存一致性税。

于 2013-01-16T17:53:59.693 回答
-1

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

问候安德烈

于 2013-01-15T12:25:15.747 回答