java - Java Spring Boot 会以线程安全的方式在 Redis 中设置和获取值吗?
问题描述
我需要存储经常更改的 ArrayList 的值,并在应用程序崩溃的情况下保留这些值。我正在开发的应用程序已经使用了 Redis 数据库,所以这似乎是一个不错的选择。
下面,我总结了一个 Spring Boot 控制器的最小示例,该控制器连接到 Redis 的 localhost 实例并使用它来存储序列化对象。可以从控制器端点修改该值,也可以通过每 5 秒运行一次的计划作业修改该值。如果您对 执行一系列获取请求localhost:8080/test
,您将看到计划的作业一次从 ArrayList 中删除一个项目。
是否有可能错过某个值,或者这里发生的不是线程安全的事情?我担心如果他们尝试修改对象或同时设置 Redis 值,计划的作业可能会与控制器端点所做的更改发生冲突,尤其是在网络速度变慢的情况下,但我不确定这是否真的会一个问题。当它在我的本地主机上运行时,一切似乎都运行良好,但我仍然持怀疑态度。
我阅读了这篇关于线程安全的文章,但它没有回答是否有任何这些东西对于这种特殊情况是必要的。我也知道Redis 读写是 atomic,但我想,如果命令以错误的顺序发送到 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<>());
. 然后仍然不是原子的,尽管这对于功能来说可能add
并不重要。您最终可能会得到(例如)以下类型的执行pop
someList.add("sample string");
someList.remove(0);
redis.set("somekey", gson.toJson(someList));
redis.set("somekey", gson.toJson(someList));
并且消息可能会令人困惑,因为它可能显示“添加了新项目。数组中有 4 个项目”、“添加了新项目。数组中有 4 个项目”、“删除了项目。数组中有 4 个项目”,因为添加/删除发生了在打印之前。
因此,对于给定代码(或类似代码)的正确功能,您必须同步方法或使用显式共享锁。有可能以错误的顺序发送命令,但在给定的示例中(假设列表是线程安全的)没有真正危险的机会,因为它只会导致相同数据的重复集。
推荐阅读
- ios - 'Branch.h' 文件未找到 Ionic 3 应用程序的 Xcode 错误
- tensorflow2.0 - 是否可以将 Tensorflow Graphics 的 Levenberg-Marquardt 优化器与 Tensorflow 2.0 模型集成?
- asp.net-core - 使用 Saml2 或 Ws Federation 在 IDM SAP 和 ADFS 上进行身份验证
- javascript - 使用 onClick 链接到 React/Semantic UI 中的组件
- c++ - C ++确定整个句子是否是回文,而不仅仅是第一个单词
- erlang - 如何在 Erlang 中将负数作为命令行参数传递
- algorithm - 具有重复项的旋转排序数组中的最小值
- monitoring - 如何promtail将json解析为标签和时间戳
- c# - 替换函数查询
- rest - 所有选定文档的 Sharepoint Online 工作流程