我正在使用一些ReentrantLock来跨多个线程同步对List的访问。我只写了一个通用的
try {
lock.lock();
... modify list here
} finally {
lock.unlock();
}
无处不在。我只是注意到,代码中的大多数列表仅由单个(gui dispatch-)线程使用。
现在我不确定在这些情况下我是否应该删除锁,因为它可能会加快我的代码速度。 ReentrantLock的速度有多快?如果线程本身是锁的“先前所有者”,即使他解锁()它,lock()操作会以某种方式更快吗?
答案 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会看到a
和b
的正确值,但无法保证在阅读c
时会看到什么。
如果取出锁,请确保您的数据结构是线程安全的,如果包含的数据(即列表中的对象)不是线程安全的,请确保在每次修改和后续检索之间你触发发生之前关系的数据,否则会发生不好的事情。
答案 4 :(得分:0)
我编写了测试代码,该代码显示ReentrantLock的速度比在单个线程上同步的速度慢10倍。
由于我的自定义TCP协议的解码器花费了300 ns的锁定速度,因此很容易。
JDK 14:
JDK 11:
我的测试:
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";
}
}