ReentrantReadWriteLock读写锁的使用——实现AB互斥、AC互斥、BC不互斥

1、什么是读写锁ReentrantReadWriter

说到ReentrantReadWriteLock,首先要做的是与ReentrantLock划清界限。它和后者都是单独的实现,彼此之间没有继承或实现的关系。

ReentrantLock 实现了标准的互斥操作,属于排他锁,也就是一次只能有一个线程持有锁,也即所谓独占锁的概念。显然这个特点在一定程度上面减低了吞吐量,实际上独占锁是一种保守的锁策略,在这种情况下任何“读/读”,“写/读”,“写/写”操作都不能同时发生。但是同样需要强调的一个概念是,锁是有一定的开销的,当并发比较大的时候,锁的开销就比较客观了。所以如果可能的话就尽量少用锁,非要用锁的话就尝试看能否改造为读写锁。

ReadWriteLock 描述的是:一个资源能够被多个读线程访问,或者被一个写线程访问,但是不能同时存在读写线程。也就是说读写锁使用的场合是一个共享资源被大量读取操作,而只有少量的写操作(修改数据)。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。

2、读写锁ReentrantReadWriter特性

公平性选择:支持非公平(默认)和公平的锁获取模式,吞吐量还是非公平优于公平
重入性:该锁支持重入锁,以读写线程为例:读线程在获取读锁之后,能够再次读取读锁,而写线程在获取写锁之后可以同时再次获取读锁和写锁
锁降级:遵循获取写锁,获取读锁再释放写锁的次序,写锁能够降级为读锁

3、读写锁ReadWriteLock的API

public interface ReadWriteLock {

    /**
     * Returns the lock used for reading.
     */
    Lock readLock();

    /**
     * Returns the lock used for writing.
     */
    Lock writeLock();
}

4、实现AB互斥、AC互斥、BC不互斥

思考这样一个需求

一个java类里面有ABC三个方法,我想在多线程的情况下,AB互斥、AC互斥、BC不互斥,有什么办法做到吗?

用读写锁实现的思路很简单,就是 B和C加上读锁,A加上写锁,就可以了,这样BC可以同时走,但B或C执行时,A不能执行,反之也是

private static final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

private static final Lock readLock = rwLock.readLock();  // 读锁
private static final Lock writeLock = rwLock.writeLock();// 写锁

public static void A() {
  try {
    writeLock.lock();
    // do something...
  } finally {
    writeLock.unlock();
  }
}

public static void B() {
  try {
    readLock.lock();
    // do something...
  } finally {
    readLock.unlock();
  }
}

public static void C() {
  try {
    readLock.lock();
    // do something...
  } finally {
    readLock.unlock();
  }
}

思考一个问题,我们默认使用的锁是非公平锁,只有当我们显示设置为公平锁的情况下,才会使用公平锁。看读写锁的实现源码,我们可以发现,读锁和写锁共用同一个等待队列,那么在采用非公平锁的情况下,如果读锁的线程执行时间比较长,并且读锁的并发比较高,那么写锁的线程是有可能永远都拿不到锁的。这个时候就需要使用公平锁了,公平锁可以实现FIFO,如果等待队列中没有节点在等待,则占有锁,如果已经存在等待节点,则返回失败,由后面的程序去将此线程加入等待队列。但是,使用公平锁的线程进行了频繁切换,而频繁切换线程,性能必然会下降的厉害,这也告诫了我们在实际的开发过程中,在需要使用公平锁的情景下,务必要考虑线程的切换频率。

猜你喜欢

转载自blog.csdn.net/ljc1026774829/article/details/79810120