ReadWriteLock 被称为读写锁,通过读读不加锁的方式区分业务,从而提高效率
读写锁与ReentLock锁的效率对比
我们用如下代码进行一个简单的对比
非公平模式(默认)
当以非公平初始化时,读锁和写锁的获取的顺序是不确定的。非公平锁主张竞争获取,可能会延缓一个或多个读或写线程,但是会比公平锁有更高的吞吐量。
公平模式
当以公平模式初始化时,线程将会以队列的顺序获取锁。当当前线程释放锁后,等待时间最长的写锁线程就会被分配写锁;或者有一组读线程组等待时间比写线程长,那么这组读线程组将会被分配读锁。
建议看着demo自己敲一下,可能会发现很多新的问题,记忆也更牢固
package ReentrantLock和LongAdder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @program: solution
* @description: 演示ReadWriteLock的使用
* @author: Wang Hai Xin
* @create: 2022-11-14 10:10
**/
public class ReadWritLockT {
/**/
static ReentrantLock lock = new ReentrantLock();
static int value = 0;
/*读写锁,ReentrantReadWriteLock 是读写锁ReadWriteLock的一种实现*/
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/*可以通过 redLocke和writeLock分出两把锁使用*/
static Lock read = readWriteLock.readLock();
static Lock write = readWriteLock.writeLock();
public static void main(String[] args) {
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String format = simpleDateFormat.format(date);
Runnable writeRunnable = () -> write(lock, 1);
// Runnable writeRunnable = () -> write(write, 1);
Runnable readRunnable = () -> read(lock);
//
// Runnable readRunnable = () -> read(read);
ArrayList<Thread> threads = new ArrayList<>();
for (int i = 0; i < 18; i++) {
threads.add(
new Thread(readRunnable)
);
}
for (int i = 0; i < 2; i++) {
threads.add(
new Thread(writeRunnable)
);
}
for (int i = 0; i < threads.size(); i++) {
threads.get(i).start();
}
for (int i = 0; i < threads.size(); i++) {
try {
threads.get(i).join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("ReentrantLock开始时间"+format);
Date date1 = new Date();
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String format1 = simpleDateFormat1.format(date1);
System.out.println("ReentLock 结束时间:" + format1);
}
public static void read(Lock lock) {
try {
lock.lock();
Thread.sleep(1000);
System.out.println("读数据结束");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public static void write(Lock lock, int i) {
try {
lock.lock();
Thread.sleep(1000);
value += i;
System.out.println("写操作完成");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
ReentrantLock运行结果
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
写操作完成
写操作完成
ReentrantLock开始时间2022-11-14 10:46:20 217
ReentLock 结束时间:2022-11-14 10:46:40 476
使用ReentrantReadWriteLock运行的结果为
// Runnable writeRunnable = () -> write(lock, 1);
//
Runnable writeRunnable = () -> write(write, 1);
//
// Runnable readRunnable = () -> read(lock);
Runnable readRunnable = () -> read(read);
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
读数据结束
写操作完成
写操作完成
ReentrantLock开始时间2022-11-14 10:48:45 604
ReentLock 结束时间:2022-11-14 10:48:48 701
可以看出 在特定条件下,ReentrantReadWritLock的效率要比Reentrantlock的效率高很多。
使用场景应该很好想了,就是读多写少
的场景可以使用.
笔者不太推荐使用,
因为维护起来会很麻烦,如果业务逻辑复杂,后来的人很有可能就在读的逻辑中操作了数据。
要实现一个读写锁,需要考虑很多细节,其中之一就是锁升级和锁降级的问题。什么是升级和降级呢?
在不允许中间写入的情况下,写入锁可以降级为读锁吗?读锁是否可以升级为写锁,优先于其他等待的读取或写入操作?简言之就是说,锁降级:从写锁变成读锁;锁升级:从读锁变成写锁,ReadWriteLock是否支持呢?
ReadWriteLock 不支持锁升级
ReadWriteLock 支持锁降级,但是!! 并不会自动释放写锁,需要显式的释放写锁,否则其它线程永远获取不到写锁