ReentrantLock:在单线程应用程序中锁定/解锁速度

时间:2011-11-27 15:22:06

标签: java multithreading performance locking reentrancy

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

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

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

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

5 个答案:

答案 0 :(得分:3)

无论速度有多快,如果只是非常小的话,肯定会比没有锁定慢。我个人更喜欢正确性超过速度增益(这不会很多)并保持代码的方式(特别是如果它已经过测试),除非你已经确定“锁定”是一个瓶颈。

答案 1 :(得分:2)

3件事:

  • lock.lock()应位于try区块之外。

  • 如果你在单核CPU上运行锁定非常便宜。然后,取决于CPU架构,获取/释放可能是便宜的或不是那么多。 Nehalem +很好。

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

答案 2 :(得分:1)

如果没有争用,获取和释放锁定相当便宜。我想你不必担心性能影响。

答案 3 :(得分:0)

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

可能存在比性能更大的问题。 <强>可见性

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

你似乎缺少的同步方面是,除非使用一些特定的结构(锁/易变量/最终字段),否则不能保证一个线程会看到另一个线程对内存做了什么。 / p>

在这种情况下,您的保证由锁定给出:当线程T1获得锁定L时,保证在T2释放锁定L之前它将看到线程T2对存储器所做的所有修改。 / p>

   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时会看到什么。

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

答案 4 :(得分:0)

我编写了测试代码,该代码显示ReentrantLock的速度比在单个线程上同步的速度慢10倍。

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

JDK 14:

  • ReentrantLock:32.34 ns
  • 同步:3.48 ns

JDK 11:

  • ReentrantLock: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";
    }
}