【问题标题】:waiting for results if necessary必要时等待结果
【发布时间】:2026-01-23 01:45:01
【问题描述】:

我正在创建一个网页,其中包含一些动态生成的图像。

在我的页面请求处理中,我创建所有图像并将它们存储在内存缓存中,直到它们随后被浏览器请求。

public class CachedImage
{
    byte[] data;
    Date created;
}

目前,我的图片缓存本质上是HashMap<Integer, CachedImage>

问题是图像生成需要时间,我想在所有图像完成生成之前开始渲染页面。

所以我想在线程池中生成图像,并在请求时返回数据(如果它已准备好)或等到数据准备好后再返回。

谁能为这种机制想出一个整洁的模型?

图像很小,我很满意现在将整个图像缓存在内存中的方法。

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    在生成页面时,为页面将显示的每个图像选择一个新的唯一标识符。这可能是一些简单的东西,比如通过递增AtomicInteger 获得的数字,或者它可能是更复杂的东西,比如 UUID,以防止用户猜测其他用户的图像 URL。将这些唯一标识符放入客户端将用于检索图像的 URL。

    一旦您选择了图像的标识符,请构造一个将生成并返回图像的 Callable,并将其提交给 ThreadPoolExecutor 以异步运行。这会给你一个Future,它可以用来检索结果。将 Future 保存在地图中,以图像的标识符为键。

    稍后,当客户端请求图像时,您可以获取图像标识符并在地图中查找它以找到关联的 Future 对象。在 Future 上调用 get() 将返回图像,如有必要,等待生成器完成。 (如果在地图中没有找到请求的标识符,则返回 404 错误。)

    为避免旧图像填满服务器的内存,您可能需要在几分钟后将其丢弃。为此,每次您创建一个任务并将其 Future 存储到可用图像的映射中时,您可以将一个任务放入 DelayQueue 中,这将在适当的延迟后从映射中删除该条目。使用守护线程从该队列中获取项目并在循环中对其执行操作。

    从地图中移除 Future 时,最好在 Future 上调用 cancel(true),以防生成器由于某种原因仍在运行。 (否则,即使无法再访问该图像,它也会继续运行。)

    【讨论】:

    • 几乎是我现在所拥有的。
    【解决方案2】:

    听起来你需要类似 futuretask 的东西

    http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Future.html

    只需启动一堆它们,当 isDone 为真时,您可以拉回结果,或者您甚至可以通过简单地调用 get() 来阻止等待它们完成。

    【讨论】:

    • 现在响应速度要快得多,而且由于一半的图像在页面加载到浏览器时已经准备好,所以这个解决方案比其他两个建议的解决方案要快得多。
    • 如果可以的话,我会加倍支持,但 wyzard 提供了更多信息。不过,你让我走上了正确的道路,谢谢。
    【解决方案3】:

    您可以从当前请求中删除整个逻辑,并使用异步请求 (ajax) 渲染图像,这甚至可以让您拥有一个“正在加载...”图标和控制超时等

    【讨论】:

    • 好主意,但是图像渲染参数的复杂性使得分离单个图像变得困难。我已经呼吁一代需要在某一时刻初始化,并且必须坚持下去。
    【解决方案4】:

    当客户端(浏览器)请求一个网页时,它实际上并没有得到图像。当它呈现页面并查找标签时,浏览器会针对每个图像向服务器发出单独的请求。这意味着您可以在图像存在之前发送 HTML 页面。

    因此,触发您的 HTML 响应,将图像创建请求发送到您的图像服务。当浏览器解析响应并从 Web 服务器请求图像时。

    您可以让另一个线程池通过在单个线程中接受请求来响应图像请求,在图像准备好时响应,从而将其提升一个档次。如果您只是尽可能快地生成图像并让您的网络服务器响应图像请求,那么您的用户将收到尚未生成的图像的 404 响应。

    【讨论】: