读写锁
ReentrantReadWriteLock,其核心就是实现
读写分离,多哦,突然记得之前学的COW容器也是读写分离的。好了,读写分离有什么好处了,就是在高并发访问下,尤其是读多写少的情况下,性能要远高于重入锁。
之前学synchronized、ReentrantLock的时候,我们都知道,同一时间内,只能有一个线程进行访问被锁定的代码,那么读写锁则不同,其本质是分成两个锁,就是读锁和写锁。在读锁下,多个线程可以并发的进行访问,但是在写锁的时候,只能一个一个的访问。只有是读的情况下是可以并发访问的,而多个线程访问写或者多个线程分别访问读和写,都是一个一个地访问的。
有这么一个口诀:读读共享,写写互斥,读写互斥。
1、下面的例子是看如何使用,而且测一下是不是读读共享。
public class UseReentrantReadWriteLock {
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
//原来读锁和写锁都是用ReentrantReadWriteLock创建出来的,哪个方法需要什么锁,就调用什么锁的lock()方法,不过最后记得要释放锁。
private ReadLock rLock = rwLock.readLock();
private WriteLock wLock = rwLock.writeLock();
public void read(){ //读操作
try{
//因为这里模拟的是读操作,所以调用的是读锁的lock方法
rLock.lock();
System.out.println("线程:"+Thread.currentThread().getName()+"进入读操作");
Thread.sleep(2000);
}catch(Exception e){
e.printStackTrace();
}finally{
System.out.println("线程:"+Thread.currentThread().getName()+"执行完读操作");
rLock.unlock();
}
}
public void write(){ //写操作
try{
//因为这里模拟的是写操作,所以调用的是写锁的lock方法。
wLock.lock();
System.out.println("线程:"+Thread.currentThread().getName()+"进入写操作");
Thread.sleep(2000);
}catch(Exception e){
e.printStackTrace();
}finally{
System.out.println("线程:"+Thread.currentThread().getName()+"执行完写操作");
wLock.unlock();
}
}
public static void main(String[] args) {
UseReentrantReadWriteLock urLock = new UseReentrantReadWriteLock();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
urLock.read();
}
},"t1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
urLock.read();
}
},"t2");
t1.start();
t2.start();
}
}
执行结果:可以发现,线程1和线程2是同时获得读锁然后进入读方法的,也是同时的完成。所以说读读是共享的,不存在单个线程获得锁执行方法的说法。
线程:t2进入读操作
线程:t1进入读操作
线程:t2执行完读操作
线程:t1执行完读操作
2、那么下面是测试写写是否是互斥的。
我们将上面的线程1和线程2都换成调用写操作就可以测试了。
UseReentrantReadWriteLock urLock = new UseReentrantReadWriteLock();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
urLock.write();
}
},"t1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
urLock.write();
}
},"t2");
t1.start();
t2.start();
执行结果:可以看到,线程2先获取了写锁然后执行写方法,当线程2执行完释放锁后,线程1才获得写锁然后执行写操作。所以说写写是互斥的。
线程:t2进入写操作
线程:t2执行完写操作
线程:t1进入写操作
线程:t1执行完写操作
3、最后看一下读写互斥,我们将线程1改为读操作,线程2是写操作。
UseReentrantReadWriteLock urLock = new UseReentrantReadWriteLock();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
urLock.read();
}
},"t1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
urLock.write();
}
},"t2");
t1.start();
t2.start();
执行结果:我们可以看到,写操作和读操作确实是互斥的,只有其中一个操作做完,另一个操作才能进行。
线程:t2进入写操作
线程:t2执行完写操作
线程:t1进入读操作
线程:t1执行完读操作
读写锁还挺有意思的。。