关于Redission读写锁

目录

什么是读写锁:

读写锁分两种:

1.读锁(共享锁):

2.写锁(排它锁):

优点:

仍有问题:

改进:

读写锁使用:

修改时:

读取信息时:

生产者:

消费者:


思考一个场景,当我们的一个数据正在修改的时候,正好有用户来访问,我们对这个数据的相关统计监控数据就无法正确进行。

什么是读写锁:

读写锁是一种用于管理对共享资源的访问的同步机制,允许多个线程同时读取共享资源,但在写入时保证独占访问,以确保数据的一致性和完整性。

读写锁分两种:
1.读锁(共享锁):

多个线程可以同时获取读锁,用于并发读取共享资源。读锁在没有写锁的情况下可以被多个线程持有。

2.写锁(排它锁):

写锁是独占的,一旦一个线程获取了写锁,其他线程无法同时获取读锁或者写锁。写锁用于修改共享资源,确保在写入时没有其他线程能有访问

优点:

读写锁的优势在于它允许多个线程同时读取共享资源,提高了读取的并发性,从而提升了性能。但是,在写入时必须独占资源,以确保数据的一致性。这种锁的使用场景适用于读操作频繁,写操作较少的情况。

仍有问题:

当我们在修改数据的时候拿到了写锁,读锁获取不到,难道这段时间内用户一直不允许访问么?

如果获取不到就一直等着,会有很多线程堆在这里,应用会垮掉。

如果获取不到就返回,那我们后续的数据统计监控,记录信息就不精准

改进:

我们用到了延迟队列:

当我们获取读锁失败的时候,把东西扔给延迟队列,可以设置一个等待时间,时间到了再去统计,如果还没获取到锁,那就再扔进去,等待一定时间后统计。

读写锁使用:
修改时:
java 复制代码
            //拿到写锁
            RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(String.format(LOCK_GID_UPDATE_KEY, requestParam.getFullShortUrl()));
            RLock rLock = readWriteLock.writeLock();
            if (!rLock.tryLock()) {
                throw new ServiceException("短链接正在被访问,请稍后再试...");
            }
            try {
            } 
            finally {
            rLock.unlock();
            }
读取信息时:
java 复制代码
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(String.format(LOCK_GID_UPDATE_KEY, fullShortUrl));
        //先拿读锁
        RLock rLock = readWriteLock.readLock();
        if (!rLock.tryLock()) {
            delayShortLinkStatsProducer.send(statsRecord);
            return;
        }
         try {
         } 
         catch (Throwable ex) {
            throw new ServiceException("监控器异常");
        }
         finally {
         rLock.unlock();
         }
生产者:
java 复制代码
public class DelayShortLinkStatsProducer {

    private final RedissonClient redissonClient;

    /**
     * 发送延迟消费短链接统计
     *
     * @param statsRecord 短链接统计实体参数
     */
    public void send(ShortLinkStatsRecordDTO statsRecord) {
        RBlockingDeque<ShortLinkStatsRecordDTO> blockingDeque = redissonClient.getBlockingDeque(DELAY_QUEUE_STATS_KEY);
        RDelayedQueue<ShortLinkStatsRecordDTO> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
        delayedQueue.offer(statsRecord, 5, TimeUnit.SECONDS);
    }
}
消费者:
java 复制代码
@Component
@RequiredArgsConstructor
public class DelayShortLinkStatsConsumer implements InitializingBean {

    private final RedissonClient redissonClient;
    private final ShortLinkService shortLinkService;

    public void onMessage() {
        Executors.newSingleThreadExecutor(
                        runnable -> {
                            Thread thread = new Thread(runnable);
                            thread.setName("delay_short-link_stats_consumer");
                            thread.setDaemon(Boolean.TRUE);
                            return thread;
                        })
                .execute(() -> {
                    RBlockingDeque<ShortLinkStatsRecordDTO> blockingDeque = redissonClient.getBlockingDeque(DELAY_QUEUE_STATS_KEY);
                    RDelayedQueue<ShortLinkStatsRecordDTO> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
                    for (; ; ) {
                        try {
                            ShortLinkStatsRecordDTO statsRecord = delayedQueue.poll();
                            if (statsRecord != null) {
                                shortLinkService.shortLinkStats(null, null, statsRecord);
                                continue;
                            }
                            LockSupport.parkUntil(500);
                        } catch (Throwable ignored) {
                        }
                    }
                });
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        onMessage();
    }
}

后续我们可以使用Rabbit或者别的MQ的延迟队列来进行更好的替换优化

相关推荐
科技小花16 小时前
全球化深水区,数据治理成为企业出海 “核心竞争力”
大数据·数据库·人工智能·数据治理·数据中台·全球化
X566118 小时前
如何在 Laravel 中正确保存嵌套动态表单数据(主服务与子服务)
jvm·数据库·python
FQNmxDG4S18 小时前
Java多线程编程:Thread与Runnable的并发控制
java·开发语言
虹科网络安全19 小时前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库
2301_7717172119 小时前
解决mysql报错:1406, Data too long for column
android·数据库·mysql
axng pmje19 小时前
Java语法进阶
java·开发语言·jvm
rKWP8gKv719 小时前
Java微服务性能监控:Prometheus与Grafana集成方案
java·微服务·prometheus
老前端的功夫19 小时前
【Java从入门到入土】28:Stream API:告别for循环的新时代
java·开发语言·python
qq_4352879219 小时前
第9章 夸父逐日与后羿射日:死循环与进程终止?十个太阳同时值班的并行冲突
java·开发语言·git·死循环·进程终止·并行冲突·夸父逐日
小江的记录本19 小时前
【Kafka核心】架构模型:Producer、Broker、Consumer、Consumer Group、Topic、Partition、Replica
java·数据库·分布式·后端·搜索引擎·架构·kafka