ReentrantReadWriteLock 读写锁的使用

基本讲解与使用

① ReadWriteLock同Lock一样也是一个接口,提供了readLock和writeLock两种锁的操作机制,一个是只读的锁,一个是写锁。

读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的(排他的)。 每次只能有一个写线程,但是可以有多个线程并发地读数据。

所有读写锁的实现必须确保写操作对读操作的内存影响。换句话说,一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容。

理论上,读写锁比互斥锁允许对于共享数据更大程度的并发。与互斥锁相比,读写锁是否能够提高性能取决于读写数据的频率、读取和写入操作的持续时间、以及读线程和写线程之间的竞争。

② 使用场景

假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。

例如,最初填充有数据,然后很少修改的集合,同时频繁搜索(例如某种目录)是使用读写锁的理想候选项。

在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写。这就需要一个读/写锁来解决这个问题。

③ 互斥原则:

  • 读-读能共存,
  • 读-写不能共存,
  • 写-写不能共存。

① ReentrantReadWriteLock拥有的特性

  • 1.1获取顺序(公平和非公平)
    ReentrantReadWriteLock不会为锁定访问强加读或者写偏向顺序,但是它确实是支持可选的公平策略。

  • 非公平模式(默认)
    构造为非公平策略(缺省值)时,读写锁的入口顺序未指定,这取决于可重入性约束。持续竞争的非公平锁可以无限期地延迟一个或多个读写器线程,但通常具有比公平锁更高的吞吐量。

  • 公平模式
    当构造为公平策略时,线程使用近似的到达顺序策略(队列策略)争夺输入。当释放当前持有的锁时,要么最长等待的单个写入线程将被分配写锁,或者如果有一组读取线程等待的时间比所有等待的写入线程都长,那么该组读线程组将被分配读锁。

如果写入锁被占有,或者存在等待写入线程,则试图获取公平读取锁(非可重入)的线程将阻塞。直到当前等待写入线程中最老的线程获取并释放写入锁之后,该线程才会获取读取锁。当然,如果等待的写入线程放弃等待,剩下一个或多个读取器线程作为队列中最长的等待器而没有写锁,那么这些读取器将被分配读锁。

试图获得公平写锁(非可重入)的线程将阻塞,除非读锁和写锁都是空闲的(这意味着没有等待的线程)。(请注意,非阻塞 ReadLock#tryLock()和{@link WriteLock#tryLock()方法不遵守此公平策略设置,并且如果可能的话将立即获取锁,而不管等待的线程)。

package com.coderman.lock.lock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 读锁可以同时获取,因此读线程可以同时进行,但是读和写是不能同时进行的.
 * 并且默认是采用公平锁的方式,不可插队.
 * 读写锁的测试
 * @Author zhangyukang
 * @Date 2020/7/16 11:03
 * @Version 1.0
 **/
public class ReadWriteLockTest {
    public static void main(String[] args) {
        TicketContainer ticketContainer = new TicketContainer();
        new Thread(()->{ticketContainer.read();},"readThread1").start();
        new Thread(()->{ticketContainer.read();},"readThread2").start();
        new Thread(()->{ticketContainer.read();},"readThread3").start();
        new Thread(()->{ticketContainer.read();},"readThread4").start();
        new Thread(()->{ticketContainer.write();},"writeThread1").start();
        new Thread(()->{ticketContainer.write();},"writeThread2").start();
        new Thread(()->{ticketContainer.read();},"readThread5=====").start();
        new Thread(()->{ticketContainer.read();},"readThread6=====").start();
        new Thread(()->{ticketContainer.write();},"writeThread3").start();
        new Thread(()->{ticketContainer.write();},"writeThread4").start();
    }
}

class TicketContainer{

    private ReentrantReadWriteLock reentrantReadWriteLock=new ReentrantReadWriteLock();

    //读锁
    private ReentrantReadWriteLock.ReadLock readLock=reentrantReadWriteLock.readLock();
    //写锁
    private ReentrantReadWriteLock.WriteLock writeLock=reentrantReadWriteLock.writeLock();

    public void read(){
        readLock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"正在读取数据");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName()+"读取数据完成");
            readLock.unlock();
        }
    }

    public void write(){
        writeLock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"正在写入数据");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName()+"写入数据完成");
            writeLock.unlock();
        }
    }
}

# Java   多线程   JUC  

评论

公众号:mumuser

企鹅群:932154986

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×