最佳 Java 线程安全对象池

发布于 2024-12-25 05:27:31 字数 433 浏览 6 评论 0原文

我对 Java 的并发库不太熟悉,因此对于以下问题,我通常只编写自己的互斥体管理代码,但我担心对于 servlet 流量,互斥体会减慢系统速度。

第一个需要是,对于一组有限的 String 键,我需要首先查找,否则创建并发布一个昂贵的对象。这意味着在一个简单的实现中存在一个全局互斥体。还有更好的吗?

第二个需求是每个昂贵的对象都有一个等效工作人员的软池,其中任何一个都足以执行。创造这些工人的成本比为工人建造工厂要便宜,但它们仍然很昂贵,需要集中起来。一个幼稚的实现将为每个工厂拥有一个互斥锁,并从软缓存中检出一个工作线程,或者在没有可用的情况下创建它。但是,随着大量 Servlet 调用使用同一工厂(可能),该互斥体也将成为争论点。

当然,对于两个互斥体,我绝对可以最大限度地减少在同步语句中花费的时间,但我正在寻找在这两种情况下更好的东西。也许两者都有一个非阻塞的解决方案?

安迪

I am not that familiar with the concurrent library of Java, so for the following problems I would normally just write my own mutex governed code, but I am concerned that with servlet traffic, the mutexes will slow down the system.

The first need is that with a finite set of String keys, I need to lookup first, otherwise create and publish an expensive object. This implies one global mutex on a naive implementation. Is there something better?

The second need is that each expensive object has a soft pool of equivalent workers, any one of which is sufficient for execution. These workers are less expensive to create than the factory for the workers, but they are still expensive and need to be pooled. A naive implementation would have one mutex per factory, and checkout a worker from the soft cache or create it if none available. But with a lot of servlet invocations using the same factory (likely) this mutex also would become a point of contention.

Of course, for the 2 mutexes, I can absolutely minimize the time spent in the synchronized statement, but I'm looking for something better in both cases. Maybe there's a nonblocking solution for both?

Andy

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

能怎样 2025-01-01 05:27:31

对于第一部分:不要将昂贵的对象直接放入 HashMap 中,而是创建一个创建成本低廉的简单包装器。然后,您基本上可以在包装器 getExpectiveObject() 方法中按需创建昂贵的对象 - 尽管如果愿意的话,显然可以立即触发创建。在任何一种情况下,您都必须同步 get 方法,但这可以通过双重检查锁定来廉价地完成 - 一般来说,我们只需用易失性读取替换普通读取,并且仅在创建对象时进行昂贵的同步。

这假设您正在使用某种 ConcurrentHashMap,因为我们需要 putIfAbsent 或某种等效方法才能使其工作(我们不想用空包装器替换现有的昂贵对象)

。现在考虑第二个问题,也许以后再考虑。

For the first part: Instead of putting the expensive objects directly into the HashMap, instead create a simple wrapper that is cheap to create. You then basically create the expensive object on demand in the wrappers getExpensiveObject() method - although it's obviously possible to trigger the creation instantly if that's preferred. In either case you have to synchronize the get method, but that can be done cheaply with double checked locking - in general we just replace a normal read with a volatile read and have the expensive synchronization only while creating the object.

This assumes you're using a ConcurrentHashMap of kinds, since we need putIfAbsent or some equivalent method for this to work (we don't want to replace an existing expensive object with our empty wrapper)

No time to think about the second problem at the moment, maybe later.

星星的轨迹 2025-01-01 05:27:31

请查看有关线程池的教程。它似乎描述了您正在寻找的内容,即工作线程池。

Take a look at this tutorial on thread pools. It appears to describe what you're looking for, a pool of worker threads.

酸甜透明夹心 2025-01-01 05:27:31

我需要首先查找,否则创建并发布一个昂贵的对象。

对此没有非阻塞的解决方案。有一个带有 get 和 put 的非阻塞映射(如果不存在),但这需要预先计算 put 值,而使用昂贵的对象则无法做到这一点。

您可能想看看“通用和并发对象pools”,它使用一些链接的阻塞队列技巧,可以避免单个互斥体争用。

I need to lookup first, otherwise create and publish an expensive object.

There is no nonblocking solution to this. There is a nonblocking map with get and put if absent, but that requires the put value to be precomputed which you can't do with an expensive object.

You might want to take a look at "Generic and concurrent object pools" which uses some linked blocking queue tricks that might avoid single mutex contention.

老旧海报 2025-01-01 05:27:31

我认为经过进一步思考我有了答案:

注意:我的答案取决于这样一个事实:工厂相对于其 String 键是幂等的,并且工人相对于他们的工厂都是幂等的,这从问题中可能并不明显。

对于第一个基于全局单键的哈希图,我利用了哈希图永远不会有删除的想法。只有新的幂等的。因此,我使用对哈希图的易失性引用,并从易失性单例变量中获取当前映射,而无需互斥体。 (现在,在 Java 中,引用的易失性获取的争用成本非常低)如果映射已过时,从某种意义上说,它不具有所有当前工厂,那也没关系。因为如果它确实有工厂(通常会预热一次),我就得到了。仅以易变的成本获得。如果没有,我现在通过进入互斥体来查询实时地图以获取“实时”地图。如果我现在从地图上找到工厂(不太可能),我就已经找到了。否则,我现在会执行非常昂贵的创建工厂操作(在互斥体之外)。完成后,我用互斥锁返回实时地图,并且由于另一个线程正在做同样的事情,它现在可能就在那里!因此,如果地图上有工厂,我就会扔掉刚刚创建的废弃工厂,并使用放在我前面的工厂。否则,我将新工厂填充到地图中,保留互斥体,然后开始使用工厂。

我认为没有更好的办法了。

在工厂软缓存上,我想我只想利用 ConcurrentLinkedQueue。然而,我的元素将是对软引用的引用。因此,我从 ConcurrentLinkedQueue 中签出一个对象,该对象具有对工作线程本身的软引用的引用。该工作线程可能已被软释放,因此我只需再次从工厂创建该工作线程,并在从 ConcurrentLinkedQueue 获取的对象中重新创建软引用,并为该工作线程设置一个软引用。因此,没有用于从工厂获取工作人员的互斥体,只有 ConcurrentLinkedQueue 签出,并且对工作人员的引用是软的。

I think I have the answer after futher thought:

NOTE: my answer depends on the fact that both the factory is idempotent relative to its String key and the workers are all idempotent relative to their factory which may not have been obvious from the question.

For the first global singleton key based hashmap, I leverage the idea that the hashmap never has deletions. Only new idempotent ones. So I use a volatile reference to the hashmap, and get the current map from the volatile singleton variable without a mutex. (volatile gets of references are now very contention cheap in Java) If the map is stale, in the sense that it doesn't have all of the current factories, that's ok. Because if it does have the factory (it usually will once warmed up), I've got it. With only the cost of the volatile get. If it doesn't have it, I now consult the live map with a get inside the mutex for the "live" map. If I get the factory from the map now (unlikely) I've got it. Otherwise, I now do the very expensive operation of creating the factory (outside mutexes). When done, I got back to the live map with a mutex, and due to another thread doing the same thing, it may be there now! So if the factory is there in the map, I throw away the wasted factory that I just created and use the one that was put there ahead of me. Otherwise, I stuff the new factory in the map, leave the mutex, and start using the factory.

I don't think there's better for this.

On the factory softcache, I think I just want to leverage ConcurrentLinkedQueue. However, my elements will be references to softreferences. So I checkout from the ConcurrentLinkedQueue an object that has a reference to a soft reference to the worker itself. The worker may have been softreleased, so I just create the worker from the factory again and recreate the softreference in the object that I got from the ConcurrentLinkedQueue, and set a softreference to the worker. So there's no mutex for getting a worker from the factory, just the ConcurrentLinkedQueue checkout, and the references to the worker are soft.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文