【问题标题】:Common practices to avoid timeouts / starvation in Java?在 Java 中避免超时/饥饿的常见做法?
【发布时间】:2012-04-30 07:34:29
【问题描述】:

我有一个 Web 服务,可以将文件写入磁盘并将其他内容写入数据库。每次写入整个操作需要 1-2 秒。

服务可以同时从多个客户端调用,但不太可能。假设有 20 个客户端同时调用 web 服务,写操作必须同步。在这种情况下,某些客户端可能会出现超时异常,因为它们必须等待很多秒。

有什么好的做法可以解决这类情况吗?就像现在一样,这些方法是同步的(这可能导致饥饿/超时)。

我是否应该通过删除synchronized 关键字让所有线程进入写入方法并将它们的任务放入任务队列以避免超时?这是解决这个问题的正确方法吗?

【问题讨论】:

  • 客户端好像坏了。如果服务通常需要几秒钟,但客户端超时,则客户端超时太快。或者如果客户端应该很快超时,那么超时是正常的,客户端必须优雅地处理它们。

标签: java multithreading synchronization queue


【解决方案1】:

删除synchronized 并将其单独放入任务队列对您没有帮助(因为这实际上是同步为您做的)。但是,如果您在将 Web 请求放入队列后立即对其进行响应,那么您将缩短响应时间。但是以牺牲一些可靠性为代价,因为用户会得到确认工作已经完成,而工作实际上并没有完成(系统可能在工作完成之前崩溃)。

【讨论】:

    【解决方案2】:

    弗朗西斯厄普顿的做法确实是一种公认​​的做法。

    另一个是进行更细粒度的同步。您可以同步对应该同步的确切不变量的访问,而不是同步类的所有读/写方法。

    甚至更好的是完全摆脱同步。这可以使用java.util.concurrent 包来实现。这个包引入了使用Non-Blocking Algorithms的新集合(在java中使用Compare-Ans-Swap原子指令实现)。这些集合(例如 ConcurrentHashMap)在扩展时可实现更好的吞吐量。

    您可以在this article 阅读更多相关信息。

    【讨论】:

      【解决方案3】:

      在这种类型的实现中(负载增加下的缓慢服务),您希望尽可能多地异步,包括超时处理(如果基于服务器)和所需的 I/O。不要让客户端响应线程等待这些耗时的操作,以保持服务器对新请求的响应,而是触发所需的操作(可能是动态线程池)并让回调处理结果,无论是超时、完成 I/O 还是错误。

      根据首先发生的情况发送适当的响应,但如果您发送错误/超时消息然后完成的 I/O 到达(由于 I/O 和计时器之间的竞争条件),请准备回滚 I/O )。这意味着服务器中需要事务语义。

      随着负载的增长,这个领域会变得越来越复杂,但早期的良好设计应该允许您随着负载的增长而扩展。理想情况下,客户端服务线程根本不应该阻塞。

      【讨论】:

        猜你喜欢
        • 2020-06-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-13
        • 2019-04-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多