需求是这样的:读操作很频繁,但写操作很少,在读的线程需要保证相应的代码块中读取的值不变
类似下面这种
private String ss; //下面代码块多线程操作需要保证原子性,以避免判断非空之后ss被改为null if (ss != null) { ss+="a"; Log.w(TAG, "testRoiRestore: "+ss.length()); }
通常做法是加synchronize关键字,但是其实绝大部分情况下都是不必要的,加关键字反而会影响读线程的性能。最初的想法是使用乐观锁,如AtomicReference,结果发现乐观锁无法保证代码块的原子性;后来想到了lock,ReadWriteLock适用于读多写少的情况,但网上各种文章只是说它支持多个线程同时读,在多个线程读的情况下性能优于synchronized,那么单线程读的性能如何呢?因此专门写了测试代码,来测试基本不需要同步的情况下二者的性能差异。先上测试代码:
private volatile int a;
private static final boolean useLock = false;
@Test
public void testLock() {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
long t = System.currentTimeMillis();
a = 0;
Thread thread0 = new Thread(() -> {
int count = 0;
while (true) {
if (useLock) {
readLock.lock();
print(count);
readLock.unlock();
} else {
synchronized (TAG) {
print(count);
}
}
count++;
if (count == 5000) {
break;
}
}
});
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1; i++) {
if (useLock) {
writeLock.lock();
a++;
SystemClock.sleep(100);
writeLock.unlock();
} else {
synchronized (TAG) {
a++;
SystemClock.sleep(100);
}
}
SystemClock.sleep(200);
}
});
thread0.start();
thread1.start();
try {
thread1.join();
thread0.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "testLock: ===finish=====lock?"+useLock+" " + (System.currentTimeMillis() - t));
}
private void print(int count) {
Log.d(TAG, "testLock: ====" + a + " count=" + count);
if (count % 100 == 99) {
Log.d(TAG, "testLock: release====" + a + " count=" + count);
}
SystemClock.sleep(1);
}
写线程只执行了一次写,读线程则循环读取5000次,分别测试ReadWriteLock、synchronized以及不加锁的情况下的运行时间,结果如下:
ReadWriteLock、synchronized在单线程读的时候性能相近,甚至后者性能更高一点;二者均比不加锁性能稍差。结论是:在单线程读的情况下,如需要同步,直接用synchronized就行了。如果是多线程读,使用ReadWriteLock性能更好,参考读写锁 ReadWriteLock和Synchronized的比较_天才丶小熊猫的博客-CSDN博客