likes
comments
collection
share

从有点基础开始学JUC:读写锁

作者站长头像
站长
· 阅读数 47

读写锁

概述

事实上,我们之前学习的锁其实都有一点小小的问题,那就是其锁的粒度太大了,像是Synchronized 和 ReentrantLock 都是独占锁,即在同一时刻只有一个线程获取到锁。而许多情况下,我们的许多操作并不是互斥的,完全可以支持并行执行,并不需要加锁来进行限制,十分的不灵活,那么有没有一个能够让我们更加自由的选择加锁的办法呢?

ReentrantReadWriteLock读写锁,顾名思义是一把锁分为两部分:读锁写锁其中读锁允许多个线程同时获得,是一个共享锁,因为读操作本身是线程安全的;而写锁则是排他锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的

主要解决:对共享资源有读和写的操作,且写操作没有读操作那么频繁的场景。

特点

  • 公平性:读写锁支持非公平和公平的锁获取方式,非公平锁的吞吐量优于公平锁的吞吐量,默认构造的是非公平锁
  • 可重入:在线程获取读锁之后能够再次获取读锁,但是不能获取写锁,而线程在获取写锁之后能够再次获取写锁,同时也能获取读锁
  • 锁降级:线程获取写锁之后获取读锁,再释放写锁,这样实现了写锁变为读锁,也叫锁降级

其更适合读多写少的场景。读锁可以被多个线程同时持有, 所以并发能力很高而写锁是独占锁, 在一个线程持有写锁时候, 其他线程都不能在抢占, 包含抢占读锁都会阻塞

使用

常用方法

方法描述
public ReentrantReadWriteLock()默认构造方法,非公平锁
public ReentrantReadWriteLock(boolean fair)true 为公平锁
public ReentrantReadWriteLock.ReadLock readLock()返回读锁
public ReentrantReadWriteLock.WriteLock writeLock()返回写锁
public void lock()加锁
public void unlock()解锁
public boolean tryLock()尝试获取锁

现在有个餐厅,其生意非常好,客人特别多,于是其要求来的客人必须先抢票才能进来用餐。于是有很多人想来用餐的就要去抢票,同时还有许多吃瓜群众看还有多少票剩余。我们来实现这个案例

public class Restaurant {

    private volatile int num = 6;

    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void grabTickets() {

        boolean isGet = false;

        //获取写锁
        rwLock.writeLock().lock();
        
        //锁降级,获取读锁
        rwLock.readLock().lock();
        
        try {
            if (num > 0) {
                --num;
                isGet = true;
            } else {
                System.out.println(Thread.currentThread().getName() + "没票了");
            }
        } finally {
            //释放写锁
            rwLock.writeLock().unlock();
        }
        
        try {
            if (isGet) {
                System.out.println(Thread.currentThread().getName() + "抢到了票,现在还剩下的票数为:" + num);
            }
        } finally {
            //释放读锁
            rwLock.readLock().unlock();
        }
    }

    public void getNum() {
        rwLock.readLock().lock();

        try {
            System.out.println(Thread.currentThread().getName() + "查看了现在的票数:" + num);
        } finally {
            rwLock.readLock().unlock();
        }

    }

}

我们这里在抢票时,为了其他线程可以更快地查看当前票数,我们采用锁降级。

然后是我们执行的代码

public class RestaurantTest {

    public static void main(String[] args) {
        Restaurant restaurant = new Restaurant();

        for (int i = 0; i < 10; ++i) {
            new Thread(restaurant::grabTickets, "食客" + (i + 1)).start();
            new Thread(restaurant::getNum, "吃瓜群众" + (i + 1)).start();
        }

    }

}

来看一下执行结果

用户1抢到了票,现在还剩下的票数为:5
吃瓜群众1查看了现在的票数:5
吃瓜群众2查看了现在的票数:5
用户2抢到了票,现在还剩下的票数为:4
用户3抢到了票,现在还剩下的票数为:3
吃瓜群众3查看了现在的票数:3
用户4抢到了票,现在还剩下的票数为:2
吃瓜群众6查看了现在的票数:2
吃瓜群众4查看了现在的票数:2
吃瓜群众5查看了现在的票数:2
用户7抢到了票,现在还剩下的票数为:1
用户6抢到了票,现在还剩下的票数为:0
用户5没票了
吃瓜群众9查看了现在的票数:0
吃瓜群众7查看了现在的票数:0
用户9没票了
用户8没票了
吃瓜群众8查看了现在的票数:0
用户10没票了
吃瓜群众10查看了现在的票数:0

进程已结束,退出代码0

这就是这篇文章的内容了,欢迎大家的讨论,如有错漏,也请指出,谢谢~

转载自:https://juejin.cn/post/7244466524559900733
评论
请登录