在多线程Java应用程序中创建缓存的深层副本

时间:2019-02-16 12:38:52

标签: java multithreading performance deep-copy

设置

我有一个多线程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
    }

}

问题

其中一个线程使用来自缓存的数据“ d1”在时间戳“ t”开始执行任务“ A”。在时间“ t + t1”,缓存数据将更新为“ d2”。线程现在开始使用数据“ d2”来完成其余任务。任务在“ t + t1 + t2”处完成。一半的任务使用不同的数据完成。这将导致任务的无效结果。

当前方法

每个线程将创建缓存的深层副本,然后使用深层副本使用以下方法之一(性能最佳)执行任务:深层副本:

How do you make a deep copy of an object in Java?

Deep clone utility recommendation

限制

  1. 使用深层复制进行克隆将创建数千个对象,这些对象可能会使JVM崩溃。

  2. 所有克隆方法在性能上都不理想。

1 个答案:

答案 0 :(得分:0)

对于您的用例,从database.getData();返回新的缓存是更好的选择。因为如果选择这种方式,则只需要每15秒创建一次新的缓存对象。如果选择克隆每个任务中的缓存,则必须在15秒内创建4501缓存对象。显然,返回新的缓存对象是正确的选择。

如果您提供的代码与项目中的代码相同,那么我相信database.getData();方法会更改单个缓存对象的内容,而不是返回新的缓存对象。如果从此方法返回新的缓存对象,则问题将得到解决。