JavaREST优化数据结构访问

JavaREST优化数据结构访问,java,multithreading,concurrency,Java,Multithreading,Concurrency,我有一个JavaREST应用程序,其中一个端点总是处理ConcurrentMap。我正在做负载测试,当负载测试开始增加时,情况真的很糟糕 为了提高应用程序的效率,我可以实施哪些策略 我是否应该使用Jetty线程,因为它是我正在使用的服务器?还是主要是代码?或者两者都有 成为瓶颈的方法如下 基本上,我需要从给定的文件中读取一些行。我无法将其存储在数据库中,因此我提出了一个映射处理。但是,我知道,对于大型文件,不仅需要很长时间才能到达行,而且我还冒着这样的风险,即当地图有许多条目时,它将消耗大量内存

我有一个JavaREST应用程序,其中一个端点总是处理
ConcurrentMap
。我正在做负载测试,当负载测试开始增加时,情况真的很糟糕

为了提高应用程序的效率,我可以实施哪些策略

我是否应该使用Jetty线程,因为它是我正在使用的服务器?还是主要是代码?或者两者都有

成为瓶颈的方法如下

基本上,我需要从给定的文件中读取一些行。我无法将其存储在数据库中,因此我提出了一个映射处理。但是,我知道,对于大型文件,不仅需要很长时间才能到达行,而且我还冒着这样的风险,即当地图有许多条目时,它将消耗大量内存

dict
ConcurrentMap

public String getLine(int lineNr) throws IllegalArgumentException {
    if (lineNr > nrLines) {
        throw new IllegalArgumentException();
    }

    if (dict.containsKey(lineNr)) {
        return dict.get(lineNr);
    }

    synchronized (this) {
        try (Stream<String> st = Files.lines(doc.toPath())

            Optional<String> optionalLine = st.skip(lineNr - 1).findFirst();

            if (optionalLine.isPresent()) {
                dict.put(lineNr, optionalLine.get());
            } else {
                nrLines = nrLines > lineNr ? lineNr : nrLines;
                throw new IllegalArgumentException();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return cache.get(lineNr);
    }
公共字符串getLine(int-lineNr)引发IllegalArgumentException{
如果(线路编号>线路编号){
抛出新的IllegalArgumentException();
}
if(dict.containsKey(线路编号)){
返回dict.get(lineNr);
}
已同步(此){
try(Stream st=Files.lines(doc.toPath())
可选optionalLine=st.skip(lineNr-1).findFirst();
if(optionalLine.isPresent()){
dict.put(lineNr,optionalLine.get());
}否则{
nrLines=nrLines>lineNr?lineNr:nrLines;
抛出新的IllegalArgumentException();
}
}捕获(IOE异常){
e、 printStackTrace();
}
返回cache.get(lineNr);
}

ConcurrentMap
同步(此)
混为一谈可能不是正确的方法。java.util.concurrent包中的类是为特定用例设计的,并尝试在内部优化同步

相反,我建议首先尝试一个设计良好的缓存库,看看性能是否足够好。一个例子是。根据文档,它为您提供了一种声明如何加载数据的方法,即使是异步加载:

AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    // Either: Build with a synchronous computation that is wrapped as asynchronous 
    .buildAsync(key -> createExpensiveGraph(key));
    // Or: Build with a asynchronous computation that returns a future
    .buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));
AsyncLoadingCache缓存=caffee.newBuilder()
.最大尺寸(10_000)
.expireAfterWrite(10,时间单位:分钟)
//或者:使用包装为异步的同步计算构建
.buildAsync(键->createExpensiveGraph(键));
//或者:使用返回未来的异步计算构建
.buildAsync((键,执行器)->createExpensiveGraphAsync(键,执行器));

ConcurrentMap
同步(此)
混为一谈可能不是正确的方法。java.util.concurrent包中的类是为特定用例设计的,并尝试在内部优化同步

相反,我建议首先尝试一个设计良好的缓存库,看看性能是否足够好。一个例子是。根据文档,它为您提供了一种声明如何加载数据的方法,即使是异步加载:

AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    // Either: Build with a synchronous computation that is wrapped as asynchronous 
    .buildAsync(key -> createExpensiveGraph(key));
    // Or: Build with a asynchronous computation that returns a future
    .buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));
AsyncLoadingCache缓存=caffee.newBuilder()
.最大尺寸(10_000)
.expireAfterWrite(10,时间单位:分钟)
//或者:使用包装为异步的同步计算构建
.buildAsync(键->createExpensiveGraph(键));
//或者:使用返回未来的异步计算构建
.buildAsync((键,执行器)->createExpensiveGraphAsync(键,执行器));
此解决方案基于以下两个假设:

  • 多线程读取同一文件不是问题
  • 虽然文档中说由于阻塞,计算应该简单且短,但我相信这只是同一密钥(或bucket/stripe)访问的问题,并且只针对更新(而不是读取)?在这种情况下,这不是问题,因为我们要么成功地计算值,要么抛出
    IllegalArgumentException
  • 使用这种方法,我们可以通过将文件作为放置密钥所需的计算来实现每个密钥只打开一次文件

        public String getLine(int lineNr) throws IllegalArgumentException {
            if (lineNr > nrLines) {
                throw new IllegalArgumentException();
            }
            return cache.computeIfAbsent(lineNr, (l) -> {
                try (Stream<String> st = Files.lines(path)) {
                    Optional<String> optionalLine = st.skip(lineNr - 1).findFirst();
                    if (optionalLine.isPresent()) {
                        return optionalLine.get();
                    } else {
                        nrLines = nrLines > lineNr ? lineNr : nrLines;
                        throw new IllegalArgumentException();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            });
        }
    
    公共字符串getLine(int-lineNr)引发IllegalArgumentException{
    如果(线路编号>线路编号){
    抛出新的IllegalArgumentException();
    }
    返回cache.computeIfAbsent(lineNr,(l)->{
    try(Stream st=Files.lines(路径)){
    可选optionalLine=st.skip(lineNr-1).findFirst();
    if(optionalLine.isPresent()){
    返回optionalLine.get();
    }否则{
    nrLines=nrLines>lineNr?lineNr:nrLines;
    抛出新的IllegalArgumentException();
    }
    }捕获(IOE异常){
    e、 printStackTrace();
    }
    返回null;
    });
    }
    
    我通过生成3个线程“验证”了第二个假设,其中:

  • Thread1通过无限循环(永久块)计算键0
  • Thread2尝试在键0处放置,但从未这样做,因为Thread1会阻塞
  • Thread3尝试在键1处放置,并立即执行此操作
  • 试一试,可能是有效的,也可能是假设是错误的,很糟糕。地图在内部使用存储桶,因此即使使用不同的密钥,计算也可能成为瓶颈,因为它会锁定存储桶/条带。

    此解决方案基于以下两个假设:

  • 多线程读取同一文件不是问题
  • 虽然文档中说由于阻塞,计算应该简单且短,但我相信这只是同一密钥(或bucket/stripe)访问的问题,并且只针对更新(而不是读取)?在这种情况下,这不是问题,因为我们要么成功地计算值,要么抛出
    IllegalArgumentException
  • 使用这种方法,我们可以通过将文件作为放置密钥所需的计算来实现每个密钥只打开一次文件

        public String getLine(int lineNr) throws IllegalArgumentException {
            if (lineNr > nrLines) {
                throw new IllegalArgumentException();
            }
            return cache.computeIfAbsent(lineNr, (l) -> {
                try (Stream<String> st = Files.lines(path)) {
                    Optional<String> optionalLine = st.skip(lineNr - 1).findFirst();
                    if (optionalLine.isPresent()) {
                        return optionalLine.get();
                    } else {
                        nrLines = nrLines > lineNr ? lineNr : nrLines;
                        throw new IllegalArgumentException();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            });
        }
    
    公共字符串getLine(int-lineNr)引发IllegalArgumentException{
    如果(线路编号>线路编号){
    抛出新的IllegalArgumentException();
    }