我正在努力理解readwritelock。[这段代码只能在ide中工作。复制和粘贴。你自己试试看]
class ReadWrite {
private static final ReadWriteLock LOCK = new ReentrantReadWriteLock();
private static final Lock READ_LOCK = LOCK.readLock();
private static final Lock WRITE_LOCK = LOCK.writeLock();
private static final int[] ARR = new int[1];
int i = 0;
Integer read(){
Integer value = null;
try{
READ_LOCK.lock();
value = ARR[0];
}catch (Exception e){
e.printStackTrace();
}finally {
READ_LOCK.unlock();
}
return value;
}
void write(){
try{
WRITE_LOCK.lock();
ARR[0] = i++;
}catch (Exception e){
e.printStackTrace();
}finally {
WRITE_LOCK.unlock();
}
}
}
我想做一个性能测试。
AtomicInteger atomicInteger = new AtomicInteger(0);
ReadWrite rw = new ReadWrite();
// read 10 millions times
Runnable r1 = () -> IntStream.rangeClosed(1, 10_000_000).forEach(i -> {
if(rw.read() > 0)
atomicInteger.incrementAndGet();
});
Runnable r2 = rw::write;
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
Thread[] threads = new Thread[10];
long before = System.currentTimeMillis();
scheduledExecutorService.scheduleAtFixedRate(r2, 1, 1, TimeUnit.MICROSECONDS);
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(r1);
threads[i].start();
}
for (int i = 0; i < 10; i++) {
threads[i].join();
}
System.out.println("Time Taken :: " + (System.currentTimeMillis() - before));
System.out.println("No fo reads :: " + atomicInteger.get());
做了几次测试。
案例1:
当我使用read\u lock进行读取时,需要12秒才能完成。读取次数为100000000。
案例2:
当我对读写都使用write\u lock时(本例中不使用read\u lock),测试只需要2.5秒。读取次数为100000000。
我在想使用单独的锁应该可以提高性能。
这是怎么回事?我犯了什么错误?
2条答案
按热度按时间sirbozc51#
您正在运行read()1000万次(*10个线程)。只运行一次write()。。写操作花费了2.5秒,因为它只能在没有带读锁的线程的情况下获取写锁。
另外,正如@burak所提到的,你没有在这里测量正确的东西。
您应该使用read lock和write lock运行相同的方法一次。例如,用10个线程运行这个方法。例如,该方法将迭代100-1000万次。
此外,您正在计算在测试中创建线程的时间(这不是锁机制的一部分)。您应该在之前创建线程)
然后您将看到write lock方法比read lock慢。为什么?因为当一个线程获得写锁时,只有这个线程才能执行方法代码。在读锁的情况下,所有10个线程将并行运行该方法
rmbxnbpk2#
readwritelock的文档提到:
此外,如果读操作太短,则读写锁实现的开销(其本质上比互斥锁更复杂)可以控制执行成本,特别是当许多读写锁实现仍然通过一小段代码序列化所有线程时。最终,只有评测和测量才能确定读写锁的使用是否适合您的应用程序。
您的读取速度确实非常快,因此您可以观察到读写锁相对于简单锁的开销。
读写锁的实现涉及到什么?首先,实际上有两个锁。读锁可能被多个线程占用,这使得它不同于简单的可重入锁,并且在尝试锁定时必须检查写锁是否未被锁定。writer锁在尝试锁定时必须检查是否没有锁定的读卡器,但它在其他方面类似于单线程可重入锁。
对于细粒度的访问,比如在您的示例中,读写锁是不值得的。当访问一组数据(例如一个“页面”的数据,例如数百或数千个缓存的数据库行)时,开销可能变得微不足道。