在多线程 Java 应用程序中创建缓存的深层副本
Creating a deep copy of cache in mulithreaded Java application
设置
我有一个多线程 Java 应用程序,它每秒接收 200-300 个请求以根据请求中收到的输入执行任务 'A'(大约需要 30 毫秒)。
应用程序有一个缓存(最大大小 = 1MB),每个线程读取它以在收到的输入上执行任务 'A':
public class DataProvider() {
private HashMap<KeyObject, ValueObject> cache;
private Database database;
// Scheduled to run in interval of 15 seconds by a background thread
public synchronized void updateData() {
this.cache = database.getData();
}
public HashMap<KeyObject, ValueObject> getCache() {
return this.cache;
}
}
KeyObject 和 ValueObject 是 POJO。 ValueObject 包含另一个 POJO 的列表。
对于每个收到的请求,任务都按以下方式完成:
public class TaskExecutor() {
private DataProvider dataProvider;
public boolean doTask(final InputObject input) {
final HashMap<KeyObject, ValueObject> data = dataProvider.getCache(); // shallow copy I think
// Do Task 'A' using data
}
}
问题
其中一个线程在时间戳 't' 使用缓存中的数据 'd1' 开始执行任务 'A'。在时间 't + t1' 缓存数据更新为 'd2'。线程现在开始使用数据 'd2' 来完成剩余的任务。任务在 't+t1+t2' 完成。一半的任务是用不同的数据完成的。这将导致任务的无效结果。
当前方法
每个线程都会创建缓存的深拷贝,然后使用深拷贝执行任务,使用以下方法之一(性能最佳)执行深拷贝:
How do you make a deep copy of an object in Java?
Deep clone utility recommendation
限制
使用深拷贝克隆将创建数千个可能导致 JVM 崩溃的对象。
所有的克隆方法在性能方面看起来都不太好。
对于您的用例,return从 database.getData();
获取新缓存是更好的选择。因为如果你选择这种方式,你只需在 15 秒内创建一次新的缓存对象。如果您选择在每个任务中克隆缓存,您将不得不在 15 秒内创建 4501 个缓存对象。显然 returning 新的缓存对象是正确的选择。
如果您提供的代码与您的项目中的代码相同,我相信 database.getData();
方法会更改单个缓存对象的内容,而不是 return 创建新的内容。如果您 return 通过此方法创建一个新的缓存对象,您的问题将得到解决。
设置
我有一个多线程 Java 应用程序,它每秒接收 200-300 个请求以根据请求中收到的输入执行任务 'A'(大约需要 30 毫秒)。
应用程序有一个缓存(最大大小 = 1MB),每个线程读取它以在收到的输入上执行任务 'A':
public class DataProvider() {
private HashMap<KeyObject, ValueObject> cache;
private Database database;
// Scheduled to run in interval of 15 seconds by a background thread
public synchronized void updateData() {
this.cache = database.getData();
}
public HashMap<KeyObject, ValueObject> getCache() {
return this.cache;
}
}
KeyObject 和 ValueObject 是 POJO。 ValueObject 包含另一个 POJO 的列表。
对于每个收到的请求,任务都按以下方式完成:
public class TaskExecutor() {
private DataProvider dataProvider;
public boolean doTask(final InputObject input) {
final HashMap<KeyObject, ValueObject> data = dataProvider.getCache(); // shallow copy I think
// Do Task 'A' using data
}
}
问题
其中一个线程在时间戳 't' 使用缓存中的数据 'd1' 开始执行任务 'A'。在时间 't + t1' 缓存数据更新为 'd2'。线程现在开始使用数据 'd2' 来完成剩余的任务。任务在 't+t1+t2' 完成。一半的任务是用不同的数据完成的。这将导致任务的无效结果。
当前方法
每个线程都会创建缓存的深拷贝,然后使用深拷贝执行任务,使用以下方法之一(性能最佳)执行深拷贝:
How do you make a deep copy of an object in Java?
Deep clone utility recommendation
限制
使用深拷贝克隆将创建数千个可能导致 JVM 崩溃的对象。
所有的克隆方法在性能方面看起来都不太好。
对于您的用例,return从 database.getData();
获取新缓存是更好的选择。因为如果你选择这种方式,你只需在 15 秒内创建一次新的缓存对象。如果您选择在每个任务中克隆缓存,您将不得不在 15 秒内创建 4501 个缓存对象。显然 returning 新的缓存对象是正确的选择。
如果您提供的代码与您的项目中的代码相同,我相信 database.getData();
方法会更改单个缓存对象的内容,而不是 return 创建新的内容。如果您 return 通过此方法创建一个新的缓存对象,您的问题将得到解决。