最佳的java线程安全对象池

时间:2012-01-07 19:16:35

标签: java thread-safety mutex nonblocking pool

我对Java的并发库并不熟悉,因此对于以下问题,我通常只编写自己的互斥锁管理代码,但我担心随着servlet流量的增加,互斥锁会降低系统速度。

首先需要使用一组有限的String键,我需要先查找,否则创建并发布一个昂贵的对象。这意味着在一个天真的实现上有一个全局互斥。还有更好的东西吗?

第二个需要是每个昂贵的对象都有一个等效工作者的软池,其中任何一个都足以执行。这些工人的创建成本比工厂的工厂便宜,但它们仍然很昂贵,需要汇集。一个简单的实现将为每个工厂安装一个互斥锁,并从软缓存中检出一个工作人员,或者如果没有可用的话就创建它。但是由于很多servlet调用使用相同的工厂(可能),这个互斥锁也会成为一个争论的焦点。

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

安迪

4 个答案:

答案 0 :(得分:1)

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

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

现在没时间考虑第二个问题,也许稍后。

答案 1 :(得分:0)

看看这个tutorial on thread pools。它似乎描述了你正在寻找的东西,一个工人线程池。

答案 2 :(得分:0)

  

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

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

您可能需要查看"Generic and concurrent object pools",它使用一些可能会避免单个互斥争用的链接阻塞队列技巧。

答案 3 :(得分:0)

我想在得到进一步的思考之后我会得到答案:

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

对于第一个基于全局单例键的hashmap,我利用了hashmap永远不会删除的想法。只有新的幂等者。所以我使用了一个对hashmap的volatile引用,并从volatile单例变量中获取当前映射而没有互斥。 (在Java中,易失性的引用现在非常便宜)如果地图是陈旧的,从它没有所有当前工厂的意义上说,那没关系。因为如果它确实有工厂(它通常会一直热身),我就知道了。只有挥发性的成本得到。如果它没有它,我现在查看实时地图,在互联网中获取“实时”地图。如果我现在从地图上获得工厂(不太可能)我已经得到了它。否则,我现在进行创建工厂(外部互斥体)的非常昂贵的操作。完成后,我用互斥锁回到实时地图,由于另一个线程做同样的事情,它现在可能就在那里!因此,如果工厂在地图上,我会丢弃我刚刚创建的浪费的工厂,并使用那个放在我前面的工厂。否则,我将新工厂填入地图,离开互斥锁,然后开始使用工厂。

我认为没有比这更好的了。

在工厂softcache上,我想我只想利用ConcurrentLinkedQueue。但是,我的元素将引用软引用。所以我从ConcurrentLinkedQueue中检出一个对象,该对象引用了对worker本身的软引用。工作者可能已被软发布,因此我只是再次从工厂创建工作者并在ConcurrentLinkedQueue中重新创建对象中的软引用,并为工作者设置软引用。因此,没有互操作来从工厂获取工人,只有ConcurrentLinkedQueue结帐,并且对工人的引用是软的。