当前位置: 首页 > 知识库问答 >
问题:

在多线程环境中从Redis列表中弹出该值会将重复值返回给几个线程

叶声
2023-03-14

DB中的一些记录与cId之间有关联

private static Long DEFAULT_REDIS_OBJECTID = 0L;


        public DataObject getDataObject(C cId, M mId) {
                String redisListKey = new StringBuilder().append(LIST-).append(cId)
                        .append("-").append(mId).toString();
    if (BooleanUtils.isFalse(redisTemplate.hasKey(redisListKey))) {
                pushRedisData(cId, mId, redisListKey);
            }
        }
    Long dataId = redisTemplate.opsForList().leftPop(redisListKey);
            if (Objects.isNull(dataId ) || 0L.equals(dataId )) {
//    Again creating the key in order to make sure another thread request for this should not shouldn't go in the db as we know we dont have data in db for this combination         
redisTemplate.opsForList().leftPush(redisListKey, 0L);
                //create a new dataObject and return it
            } else {
                //Get the dataObject based on dataId and return it
            }


    public synchronized void pushRedisData(Long cId, Long mId, String redisListKey) {
            if (BooleanUtils.isFalse(redisTemplate.hasKey(redisListKey))) {
                List<Long> dataToPush = dataService.getDataIdListFromCIdAndMIdCombination(cId,
                        mId);
                if (CollectionUtils.isNotEmpty(dataToPush )) {
                    redisTemplate.opsForList().leftPushAll(redisListKey, dataToPush );
                    redisTemplate.expire(redisListKey, 5, TimeUnit.HOURS);
                } else {
                    if (redisTemplate.opsForList().size(redisListKey) == 0) {
                        redisTemplate.opsForList().leftPush(redisListKey, DEFAULT_REDIS_ITEMID);
                    }

                }

我已经使用了同步方法将记录推送到redis,这样只有一个线程能够将数据发布到redis,而其他线程只是从redis弹出数据。如果数据库中找不到任何cId的记录

问题:当我执行文件中有1000条记录的批处理作业时

共有1个答案

阎卓
2023-03-14

由于同步泄漏,您正处于竞争状态。

您尝试同步的方式存在漏洞,允许多个线程执行相同的工作。你已经注意到了。没有任何原子块可以保护两个并发线程不执行相同的语句,这里更不用说锁了。

为什么不利用Redis的属性(例如,通过一个集合)进行同步?

您可以使用Redis集,通过将特定元素添加到Redis集中,确保只有单个线程/进程能够处理该元素。Redis将响应添加该元素是否成功。此信息将帮助您通过检查响应来解决争用问题。如果可以将元素添加到集合中,那么当前线程是第一个命中dataId的线程,并且该线程可能会继续执行昂贵的工作(数据库获取,…)。

当向同步集添加元素失败时,您就知道其他进程已经在做昂贵的工作,您可以继续从缓存中查找数据。您需要知道,尽管另一个线程可能已经赢得了非阻塞同步,但另一个第一个线程并不一定要填充缓存。然后你可以:

  1. 轮询缓存,直到值存在

还要考虑更糟糕的情况:等待缓存值并阻止导入过程,或者多次查询昂贵的数据源。您可以针对这两种情况进行优化,但您需要自行决定。

 类似资料:
  • 问题内容: 我如何获得一个线程以将元组或我选择的任何值返回给Python中的父级? 问题答案: 我建议您在启动线程之前实例化Queue.Queue,并将其作为线程的args之一传递:在线程完成之前,它将结果作为参数接收到的队列中。父母可以或愿意。 队列通常是在Python中安排线程同步和通信的最佳方法:队列本质上是线程安全的消息传递工具,这是组织多任务的最佳方法!

  • 问题内容: 我有一个方法。值在内部被更改,我想将其返回给该方法。有没有办法做到这一点? 问题答案: 可以使用局部最终变量数组。该变量必须是非基本类型,因此可以使用数组。你还需要同步两个线程,例如使用CountDownLatch: 你也可以这样使用an Executor和a Callable:

  • 问题内容: 我有一个如下的Java线程: 我大约有300个ID,每隔几秒钟-我启动线程以对每个ID进行呼叫。例如。 现在,我想从每个线程收集结果,并批量插入数据库,而不是每2秒进行300次数据库插入。 知道我该如何做到吗? 问题答案: 如果要在执行数据库更新之前收集所有结果,则可以使用该方法。如daveb建议的那样,如果您一次提交一项任务,则可以完成簿记工作。

  • 问题内容: 我的android类中有以下线程。我如何从线程中获取err的值??? 我希望该值是方法的返回值,但是对于我一生来说,我无法获得该值… 问题答案: 您可以通过两种方式实现这一目标。 在 糟糕的 方式。创建一个可变对象,如整数 列表 ,然后让Thread(可运行)写入列表。您可以在外部类/方法的列表中访问值。 使用而不是。一个可以返回值

  • 问题内容: 在android中,我正在创建用于url连接的线程。在线程中,我将响应消息存储在全局声明的字符串中。当我访问method方法时,它返回null。 当我调用该方法时,它返回null。 问题答案: 您真的只想使用它,尝试一下

  • 问题内容: 我做了这样的一个波纹管: 但是当我尝试通过getTemp使用temp时我得到0 我只想使用我在线程中所做的计算将其存储在某些变量中以备后用。 问题答案: 或者简单地添加 等待线程完成,然后再获得结果。 您的问题是您试图在计算结果之前获得结果。您应该等待线程完成后才能获得结果。这个答案也许不是最好的,但是是最简单的。由于其他人已经在使用Executors课程,所以我不想重复他们的答案。但