1

我正在使用一些 ReentrantLock 来跨多个线程同步对 List 的访问。我只是写一个通用的

try {
   lock.lock();
   ... modify list here
} finally {
   lock.unlock();
}

到处。不过,我只是注意到,代码中的大多数列表将仅由单个(gui dispatch-)线程使用。

现在我不确定在这些情况下是否应该移除锁,因为它可能会加快我的代码速度。ReentrantLock 有多快?如果线程本身是锁的“先前所有者”,即使他解锁(),lock()操作是否会更快?

4

5 回答 5

3

不管它有多快,它肯定会比没有锁定要慢,如果只是一个非常小的量。除非您确定“锁定”是瓶颈,否则我个人更喜欢正确性而不是速度增益(不会很多)并保持代码原样(特别是如果它已经过测试)。

于 2011-11-27T15:30:13.503 回答
2

3件事:

  • lock.lock() 应该在try块之外。

  • 如果你在单核 CPU 上运行,锁定是非常便宜的。然后根据 CPU 架构,获取/释放可能会便宜或不便宜。Nehalem+ 还可以。

  • 如果您不需要其他任何东西的锁synchronized可能是更好的方法,因为 JVM 可以在单线程应用程序中粗化监视器和/或偏向 lock() 'em。再次,偏向锁的性能在 CPU 架构上差异很大。

于 2011-11-27T18:09:18.887 回答
1

If there is no contention, acquiring and releasing locks is quite inexpensive. I'd say you needn't worry about the performance implications.

于 2011-11-27T15:28:42.427 回答
0

我真的不明白您的应用程序是单线程还是多线程。标题说明了一件事,而从正文我们可以推断出另一件事。我假设您使用了多个线程(否则,这个问题毫无意义——为什么要在......少于 2 个线程之间使用同步?!)。

可能存在比性能更大的问题。能见度

您没有提供有关您所在的代码部分的任何详细信息...modify a list...,但是了解详细信息非常重要:取决于您是如何做到的,如果您删除锁定,可能会发生什么:一个线程修改list 并且其他线程永远不会看到这些修改(或看到部分,很可能是不一致的修改)。

您似乎缺少的同步方面是,除非使用一些特定的构造(锁/易失性变量/最终字段),否则不能保证一个线程会看到另一个线程对内存所做的事情。

在这种情况下,您的保证是由锁提供的:当线程 T1 获得锁 L 时,可以保证它会在 T2 释放锁 L 之前看到线程 T2 对内存所做的所有修改。

   T2            T1
acquires L
modifies a
modifies b
releases L
modifies c    acquires L
              reads a
              reads b
              reads c
              releases L

在这种情况下,可以保证 T1 会看到 和 的正确值ab但不能保证它在读取 时会看到什么c

如果您取出锁,请确保您的数据结构是线程安全的,并且如果包含的数据(即列表中的对象)不是线程安全的,请确保在每次修改和随后的数据检索之间您触发happens-before关系,否则会发生不好的事情。

于 2011-11-27T17:29:43.557 回答
0

我编写了测试代码,显示 ReentrantLock 比单线程同步慢 10 倍。

由于我的自定义 TCP 协议解码器花费了 300 ns,因此锁定的速度很重要。

JDK 14:

  • 重入锁:32.34 ns
  • 同步:3.48 ns

JDK 11:

  • 重入锁:30.3 ns
  • 同步:3.7 ns

我的测试:

public class MyTest
{
    private ReentrantLock lock = new ReentrantLock();
    private String s;

    @Test
    public void test()
    {
        long t1 = System.currentTimeMillis();
        for (int i = 0; i < 100_000_000; i++) {
            lock.lock();
            try {
                //
            } finally {
                lock.unlock();
            }
        }
        long t2 = System.currentTimeMillis();
        long d1 = t2 - t1;
        System.out.println("ReentrantLock: " + d1 / 100f + " ns");

        t1 = System.currentTimeMillis();
        for (int i = 0; i < 100_000_000; i++) {
            m1();
        }
        t2 = System.currentTimeMillis();
        long d2 = t2 - t1;
        t1 = System.currentTimeMillis();
        for (int i = 0; i < 100_000_000; i++) {
            m2();
        }
        t2 = System.currentTimeMillis();
        long d3 = t2 - t1;
        long d4 = d2 - d3;
        System.out.println("synchronized: " + d4 / 100f + " ns");
    }

    public synchronized void m1() {
        s = "1";
        s = "2";
        s = "3";
    }

    public void m2() {
        s = "1";
        s = "2";
        s = "3";
    }
}
于 2020-07-26T10:16:57.053 回答