在Java Web应用程序中跟踪并发文件处理

问题描述:

我有一个将任意PDF文件转换为图像的Java 1.5 Web应用程序。一次处理即使是单个PDF的所有页面也需要很长时间,因此我希望按需处理页面。在Java Web应用程序中跟踪并发文件处理

我已经read that I can use an ExecutorService在特定页面的HTTP请求到达时,在新线程中启动/排队图像生成操作。我如何确保不会在不使用单个线程执行程序的情况下对重复操作进行排队(例如,两个用户从同一PDF请求相同的页面)?我如何使用类似于同步列表的东西来跟踪工作线程正在处理哪些图像(或者什么类型的同步机制可以帮助我跟踪这些)?

+0

这是一个很好的问题;对此有几个不好的想法,但是正确的想法有点棘手。例如,您可能无法简单地使用同步,因为它可能会导致严重的性能下降。让我考虑一下,看看我能不能帮助你... – Powerslave

+0

*“例如,你可能不能简单地使用同步,因为它可能会导致严重的性能下降。”* - 你只会得到显着的性能提升如果有很多人同时请求PDF。可能情况并非如此。保守地进行同步更好......并且稍后担心潜在的瓶颈。 –

+0

@StephenC根据*过早优化*我必须同意你的看法。仍然内置的Java并发类比一个坚持使用“synchronized”关键字更好。 – Powerslave

您可以使用带有PDF标识符(例如文件路径左右)的ConcurrentHashMap<String, Future<String>>作为键和表示转换操作本身的任务作为值。

ConcurrentHashMapputIfAbsent方法可以处理比较并设置操作的问题和FutureisDone方法可以指示转换是否已经完成与否。

putIfAbsent回报null,这意味着对于一个给定的PDF转换任务还不存在,所以你需要调用ExecutorService.submit(Callable<T> task)火了你的新创建的转换任务;否则,您可以省略此步骤并等待已有的任务完成。

样机:

Future<String> conversionTask = ... // blah 
Future<String> existingTask = conversions.putIfAbsent(pdfId, conversionTask); 
if (existingTask != null) { 
    conversionTask = existingTask; 
} 
// Either way, conversion is scheduled by now. 

ExecutorService需要排队转换请求照顾。

转换完成后,您可以通过Future<V>.get()方法检索结果。

请注意,规范不允许在Java EE应用程序中产卵线程。一个常用的方法是将您的异步处理作为JMS服务分开 - Apache Camel可以帮助您在这里。

您可以使用ConcurrentSkipListSetConcurrentHashMap来跟踪哪些PDF已被处理(并且可能被缓存)或正在处理中。使用ConcurrentLinkedQueue来处理PDF到图像的请求;当工作线程将请求从队列中拉出时,它将其添加到Set/Map中,如果添加成功,则线程处理请求,如果添加失败,则请求已经在容器中。

+0

为什么当'ConcurrentXYZ'已经线程安全(并且更快)时显式同步?此外,在调用'contains'和'add'之间可以是来自另一个线程的并发写入; 'add'返回一个表示内容是否改变(添加发生)或不存在(该项目已经存在)的'boolean'值,所以在这里不使用'contains'就更好 - * compare和set *必须是原子的。 – Powerslave

+0

我同意,我会相应地编辑我的答案 –