[英]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<>());
. 然后add
和pop
仍然不是原子的,尽管这对于功能来说可能不太重要。 您可能最终会得到(例如)以下类型的执行
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.