并发之ReentrantReadWriteLock

ReentrantReadWriteLock,谓之读写分离锁。很多情况下,线程可能都只是读取资源,这并不会对资源进行更改或者破坏,但如果这样也要阻塞等待的话,显然很不合理和高效,于是有了读写分离锁,读写分离锁可以有效地减少锁竞争,提升程序性能。其实读写分离这种理念在Mysql、redis等数据库的主从复制也应用很多,目的一样,都是为了提升系统性能。
读写锁的互相约束如下:

  • 读-读:不互斥不阻塞。
  • 读-写:互斥阻塞。
  • 写-写:互斥阻塞。

ReentrantReadWriteLock实现了接口ReadWriteLock,该接口声明了只读锁和写锁两个方法。源码如下:

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}

ReentrantReadWriteLock实现了这两个方法,源码如下:

//无参构造方法,默认为非公平锁
public ReentrantReadWriteLock() {
        this(false);
    }

//带参构造方法,可以设置锁竞争是否公平
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();//这句是不是很眼熟?几乎所有的锁都是这样的
        readerLock = new ReadLock(this);//读锁
        writerLock = new WriteLock(this);//写锁
    }
//获取读锁和写锁
    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }

我们分别对读写锁进行使用,示例如下:

public class ReentrantReadWriteLockTest {
    private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private static Lock writeLock = reentrantReadWriteLock.writeLock();
    private static Lock readLock = reentrantReadWriteLock.readLock();

    public  static class Read implements Runnable{

        @Override
        public void run() {
            try {
                readLock.lock();
                System.out.println(Thread.currentThread().getName()+" 获取锁");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println(Thread.currentThread().getName()+" 释放锁");
                readLock.unlock();
            }
        }
    }
    public  static class Write implements Runnable{

        @Override
        public void run() {
            try {
                writeLock.lock();
                System.out.println(Thread.currentThread().getName()+" 获取锁");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println(Thread.currentThread().getName()+" 释放锁");
                writeLock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        for(int i=0;i<10;i++){
            new Thread(new Read(),"读锁"+i).start();
        }
        for(int i=0;i<10;i++){
            new Thread(new Write(),"写锁"+i).start();
        }
    }
}

结果:


结果

为了结果短一点,我们把读写的线程数都设为了3,从结果可以看到,读锁都先拿到了锁的许可,并没有阻塞。而写锁只有在上一个线程释放锁后才有机会获取锁的许可。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容