繁体   English   中英

Java Spring Boot 会以线程安全的方式在 Redis 中设置和获取值吗?

[英]Will Java Spring Boot Set and Get Values in Redis in a Thread Safe Way?

我需要存储经常更改的 ArrayList 的值,并在应用程序崩溃时保留这些值。 我正在开发的应用程序已经使用了 Redis 数据库,所以它似乎是一个不错的选择。

下面,我总结了一个 Spring Boot 控制器的最小示例,该控制器连接到 Redis 的 localhost 实例并使用它来存储序列化对象。 可以从控制器端点或通过每 5 秒运行一次的计划作业修改该值。 如果您对localhost:8080/test执行一系列获取请求,您将看到计划作业一次从 ArrayList 中删除项目。

是否有可能遗漏某个值,或者在这里发生一些非线程安全的事情? 我担心计划的作业可能会与控制器端点所做的更改发生冲突,如果他们尝试同时修改对象或设置 Redis 值,尤其是在网络变慢的情况下,但我不确定这是否真的会发生一个问题。 当它在我的本地主机上运行时,一切似乎都运行良好,但我仍然持怀疑态度。

我阅读这篇关于线程安全的文章,但它没有回答这些事情是否对于这种特殊情况是必要的。 我也知道Redis 读写是原子的,但我想,如果命令以错误的顺序发送到 Redis 怎么办?

我在想,如果这个实现有问题,那么 Lombok 的 @Syncronized 注释可能对 IO 的抽象方法有用。 我感谢任何投入和花费的时间。

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;

@RestController
public class Example {

    RedisClient redisClient = RedisClient.create("redis://localhost:6379/");
    StatefulRedisConnection<String, String> connection = redisClient.connect();
    RedisCommands<String, String> redis = connection.sync();

    Gson gson = new Gson();

    ArrayList<String> someList = new ArrayList<>();


    public Example() {
        if(redis.exists("somekey") == 1){
            Type collectionType = new TypeToken<Collection<VideoDAO>>(){}.getType();
            someList = new ArrayList<>(gson.fromJson(redis.get("somekey"), collectionType));
        }
    }

    @GetMapping("/test")
    public void addToSomeList(){
        someList.add("sample string");
        redis.set("somekey",gson.toJson(someList));
        System.out.println("New item added. " + someList.size() + " items in array");
    }

    @Scheduled(fixedRate = 5000)
    public void popFromSomeList() {
        if (!someList.isEmpty()) {
            someList.remove(0);
            redis.set("somekey", gson.toJson(someList));
            System.out.println("Item removed. " + someList.size() + " items in array");
        }
    }

}

我正在使用 Java 1.8。

最明显的是someList不是线程安全的,所以即使你忽略 Redis 代码也会被破坏。

假设我们使用Collections.synchronizedList(new ArrayList<>());使它成为线程安全的Collections.synchronizedList(new ArrayList<>()); . 然后addpop仍然不是原子的,尽管这对于功能来说可能不太重要。 您可能最终会得到(例如)以下类型的执行

someList.add("sample string");
someList.remove(0);
redis.set("somekey", gson.toJson(someList));
redis.set("somekey", gson.toJson(someList));

并且消息可能会令人困惑,因为它可能会显示“添加了新项目。阵列中的 4 个项目”、“添加了新项目。阵列中的 4 个项目”、“项目删除。阵列中的 4 个项目”,由于添加/删除发生在打印之前。

因此,对于给定代码(或类似代码)的正确功能,您必须同步方法或使用显式共享锁。 有可能以错误的顺序发送命令,但在给定的示例中(假设列表是线程安全的)没有真正危险的机会,因为它只会导致相同数据的重复集。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM