1.ReentrantLock和synchronized两种锁是不管读操作还是些操作都会进行上锁。但是在一些读场景下,是可以不用上锁的,变量可以同时被多个线程同时读,不会产生脏数据.这时就可以使用到JDK的读写锁。先看ReadWriteLock结构图:这是ReadWriteLock的结构图:
在来看看ReentrantLock的顶级接口Lock的结构:
这里放两个结构图主要是ReadWriteLock的API可以把锁转换为Lock锁,所以先捋清楚结构。
2.ReentrantLock看看效果:
public class ReadWriteLockTest {
//排它锁,不管读写场景都加锁
static Lock reentrantLock = new ReentrantLock();
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//获取读锁
static Lock readLock = readWriteLock.readLock();
//获取写锁
static Lock writeLock = readWriteLock.writeLock();
//共享变量
static int value = 0;
/**
* 读操作
* @param lock
*/
static int read(Lock lock){
try {
//上锁
lock.lock();
//模拟处理业务
Thread.sleep(1000);
return value;
} catch (Exception e) {
e.printStackTrace();
}finally {
if(lock != null) lock.unlock();
}
return 0;
}
/**
* 写操作
* @return
* @Parm newValue:需要修改后的值
*/
static boolean write(Lock lock, int newValue){
try {
//上锁
lock.lock();
//模拟处理业务
Thread.sleep(1000);
value = newValue;
return true;
} catch (Exception e) {
e.printStackTrace();
}finally {
if(lock != null) lock.unlock();
}
return false;
}
public static void main(String[] args) throws Exception {
//到计数器
CountDownLatch countDownLatch = new CountDownLatch(10);
long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
//让一半线程读,一半线程写
if(i % 2 == 0){
new Thread(()->{
//读操作
read(reentrantLock);
countDownLatch.countDown();
}).start();
}else {
new Thread(()->{
//读操作
write(reentrantLock, 2);
countDownLatch.countDown();
}).start();
}
}
//等待10个线程运行结束才执行,比join()灵活
countDownLatch.await();
long end = System.currentTimeMillis();
System.out.println("reentrantLock:" + (end - start));
}
}
10个线程读写都要上锁,那么总毫时理论值为10秒左右,结果也严重了我们的猜想。
2.读锁:
public static void main(String[] args) throws Exception {
//到计数器
CountDownLatch countDownLatch = new CountDownLatch(10);
long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
new Thread(()->{
//读操作
read(readLock);
countDownLatch.countDown();
}).start();
}
//等待10个线程运行结束才执行,比join()灵活
countDownLatch.await();
long end = System.currentTimeMillis();
System.out.println("readLock:" + (end - start));
}
可以发现10个线程同时读是并没有上锁的,也就是无锁状态,多线程情况下时可以提升效率的
3.写锁:
public static void main(String[] args) throws Exception {
//到计数器
CountDownLatch countDownLatch = new CountDownLatch(10);
long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
int newValue = i;
new Thread(()->{
//读操作
write(writeLock, newValue);
countDownLatch.countDown();
}).start();
}
//等待10个线程运行结束才执行,比join()灵活
countDownLatch.await();
long end = System.currentTimeMillis();
System.out.println("writeLock:" + (end - start));
}
可以发现每次写操作都是会上锁的.
总结:在多线程读取数据时情况下并不会上锁,而写锁时会进行上锁的,读场景使用读锁可以提升效率.