|
|
1
38
如果不让我的大脑完全进入状态,从你所说的内容的快速扫描来看,你似乎需要实习生()你的字符串:
否则,具有相同值的两个字符串不一定是同一对象。 注意,这可能会引入一个新的争用点,因为在VM的深处,intern()可能需要获取一个锁。我不知道现代虚拟机在这方面是什么样子,但有人希望他们是疯狂优化。 我假设您知道静态缓存仍然需要线程安全。但是,与调用getSomeDataForemail时锁定缓存而不仅仅是密钥相比,这里的争用应该很小。 回答问题更新 : 我认为这是因为字符串文字总是产生相同的对象。戴夫·科斯塔在一篇评论中指出,这甚至比这更好:文字总是产生规范的表示。因此,程序中任何地方具有相同值的所有字符串文本都将生成相同的对象。 编辑 其他人指出 在内部字符串上同步实际上是一个非常糟糕的主意 -部分原因是允许创建intern字符串以使它们永久存在,部分原因是如果程序中任何位置的多个代码位在intern字符串上同步,则这些代码位之间存在依赖关系,并且可能无法防止死锁或其他错误。 在我输入的其他答案中,正在开发通过每个键字符串存储锁对象来避免这种情况的策略。 这里有一个替代方法——它仍然使用一个单独的锁,但我们知道无论如何我们需要其中一个用于缓存,而您所说的是50个线程,而不是5000个,所以这可能不是致命的。我还假设这里的性能瓶颈是doslowthing()中的缓慢阻塞I/O,因此它将从没有序列化中受益匪浅。如果这不是瓶颈,那么:
显然,这种方法在使用前需要进行可伸缩性的浸泡测试——我什么也不保证。 此代码不要求staticcache已同步或线程安全。如果有任何其他代码(例如旧数据的定时清理)接触到缓存,则需要重新访问。 in-progress是一个虚拟值-不完全干净,但代码很简单,它节省了两个哈希表。它不处理InterruptedException,因为我不知道您的应用程序在这种情况下要做什么。另外,如果给定的键的doslowthing()始终失败,那么这个代码就不完全是优雅的,因为每个线程都会重试它。因为我不知道失败标准是什么,也不知道它们是临时的还是永久的,所以我也不处理这个问题,我只是确保线程不会永远阻塞。实际上,您可能希望在缓存中放入一个数据值,该值指示“不可用”,可能有原因,以及何时重试的超时。
每次向缓存中添加任何内容时,所有线程都会唤醒并检查缓存(不管它们使用的是什么密钥),因此使用较少争议的算法可以获得更好的性能。但是,大部分工作将在大量空闲CPU时间阻塞I/O期间进行,因此这可能不是问题。 如果为缓存及其关联锁、它返回的数据、进行中的虚拟对象以及要执行的缓慢操作定义适当的抽象,那么可以将此代码用于多个缓存。将整个事件滚动到缓存中的一个方法中可能不是一个坏主意。 |
|
|
2
25
在一个实习字符串上进行同步可能根本不是一个好主意——通过实习,字符串会变成一个全局对象,如果您在应用程序的不同部分在同一个实习字符串上进行同步,您可能会遇到非常奇怪的、基本上不可检测的同步问题,例如死锁。这似乎不太可能,但当它发生时,你真的是一团糟。作为一般规则,只有在绝对确定模块外部没有代码可能锁定本地对象的情况下才同步。 在您的情况下,可以使用同步哈希表来存储密钥的锁定对象。 例如。:
此代码有一个争用条件,其中两个线程可以将一个对象依次放入锁表中。但是,这不应该是一个问题,因为您只有一个线程调用WebService并更新缓存,这不应该是一个问题。 如果您在一段时间后使缓存无效,那么应该在从缓存中检索数据后,在锁中再次检查数据是否为空!=空情况。 或者,更简单地说,您可以使整个缓存查找方法(“getsomedatabyemail”)同步。这意味着所有线程在访问缓存时都必须同步,这可能是性能问题。但和往常一样,首先尝试这个简单的解决方案,看看它是否真的是一个问题!在许多情况下,这是不应该的,因为处理结果的时间可能比同步要长。 |
|
|
3
9
字符串是 不 很好的同步候选。如果必须对字符串ID进行同步,则可以使用该字符串创建互斥体(请参见 synchronizing on an ID “”。该算法的成本是否值得依赖于调用服务是否涉及任何重要的I/O。 也:
|
|
|
4
5
其他人则建议把弦绑起来,这样就行了。 问题是Java必须保持接口字符串。有人告诉我,即使不保存引用,它也会这样做,因为下次有人使用该字符串时,该值需要相同。这意味着把所有的字符串都放进去可能会占用内存,而你描述的负载可能是一个大问题。 我看到了两种解决方案: 您可以在另一个对象上同步 代替电子邮件,创建一个保存电子邮件的对象(比如用户对象),该对象将电子邮件的值作为变量保存。如果你已经有了另一个代表这个人的对象(比如你已经根据他们的电子邮件从数据库中提取了一些东西),你可以使用它。通过执行等值方法和HASHCODE方法,您可以确保Java在静态缓存中对对象进行相同的处理。Cub()查找数据是否已经在缓存中(您必须在缓存上同步)。 实际上,您可以为要锁定的对象保留第二个映射。像这样:
这将阻止15次在同一个电子邮件地址上提取。你需要一些东西来防止太多的条目最终出现在emaillocks映射中。使用 LRUMap 来自阿帕奇公地的人会这么做的。 这将需要一些调整,但它可能会解决您的问题。 使用其他键 如果您愿意忍受可能的错误(我不知道这有多重要),可以使用字符串的哈希代码作为键。Ints不需要被拘留。 总结 我希望这有帮助。穿线很有趣,不是吗?您还可以使用会话设置一个值,该值表示“我已经在寻找这个”并检查第二个(第三个,第n个)线程是否需要尝试创建或只是等待结果显示在缓存中。我想我有三条建议。 |
|
5
5
您可以使用1.5并发实用程序提供一个设计为允许多个并发访问的缓存,以及一个单点添加(即只有一个线程执行昂贵的对象“创建”):
显然,这不会像您希望的那样处理异常,并且缓存中没有内置的逐出。不过,也许您可以使用它作为更改静态缓存类的基础。 |
|
|
6
3
这里是一个安全的短Java 8解决方案,它使用专用锁定对象的映射来进行同步:
它有一个缺点,即键和锁对象将永远保留在映射中。 这可以这样解决:
但随后流行的密钥会不断地重新插入到地图中,并重新分配锁对象。 更新 :这样,当两个线程同时为同一个键进入同步段,但具有不同的锁时,就有可能出现竞争条件。 所以使用起来更安全、更高效 expiring Guava Cache :
注意这里假设
|
|
|
7
2
您的主要问题不仅仅是可能有多个具有相同值的字符串实例。主要的问题是,为了访问StaticCache对象,您只需要在一个监视器上进行同步。否则,多个线程最终可能会并发地修改staticcache(尽管在不同的键下),这很可能不支持并发修改。 |
|
|
8
2
呼叫:
每次调用方法时都创建一个新对象。因为该对象是您用来锁定的对象,并且对该方法的每次调用都会创建一个新对象,所以您实际上并没有根据该键同步对映射的访问。 这将进一步解释您的编辑。当您有一个静态字符串时,它就会工作。 使用intern()解决了这个问题,因为它从string类保留的内部池返回字符串,从而确保如果两个字符串相等,则使用池中的一个。见 http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html#intern() |
|
|
9
2
使用合适的缓存框架,如 ehcache . 实现一个好的缓存并不像一些人认为的那么容易。 关于string.intern()是内存泄漏源的注释,这实际上不是真的。 内部字符串 是 垃圾收集,这可能需要更长的时间,因为在某些JVM(Sun)上,它们存储在Perm空间中,而Perm空间只被完整的GC接触到。 |
|
|
10
2
这个问题在我看来有点太宽泛了,因此它提出了同样宽泛的答案。所以我会尽力回答 the question 我已被重定向,很遗憾,其中一个已作为副本关闭。
在(外部)
类不使用的并发版本
请注意,语义
一个可能适用于这种情况的用法示例,操作描述
|
|
|
11
1
这相当晚了,但是这里有很多错误的代码。 在本例中:
同步的作用域不正确。对于支持get/put API的静态缓存,至少应在get和getifabsentput类型操作周围进行同步,以安全访问缓存。同步的范围将是缓存本身。 如果必须对数据元素本身进行更新,则会添加一个额外的同步层,该层应该位于各个数据元素上。 可以使用SynchronizedMap代替显式同步,但必须注意。如果使用了错误的API(get和put而不是putifaste),那么尽管使用了同步映射,操作将没有必要的同步。注意使用putifastence带来的复杂性:要么,即使在不需要的情况下(因为在检查缓存内容之前,Put无法知道是否需要Put值),也必须计算出Put值,要么需要小心使用委托(例如,使用Future,它有效,但有点不匹配;请参见下文)。如有需要,按需取得看跌价值。 期货的使用是有可能的,但似乎相当尴尬,而且可能有点过于工程化。未来的API是异步操作的核心,特别是对于可能无法立即完成的操作。涉及到未来很可能会增加一层线程创建——额外的可能不必要的复杂性。 这种类型的操作使用Future的主要问题是,Future内在地与多线程联系在一起。在不需要新线程的情况下使用future意味着忽略了很多future机制,使其成为这种使用的过于沉重的API。 |
|
|
12
0
为什么不呈现一个静态的HTML页面,该页面将被提供给用户,并每隔X分钟重新生成一次? |
|
|
13
0
如果您不需要,我还建议您完全取消字符串连接。
缓存中是否还有其他使用电子邮件地址的对象/类型需要在密钥开头添加额外的“数据”? 如果不是的话,我就这么做
你也可以避免所有额外的字符串创建。 |
|
|
14
0
字符串对象上的其他同步方式:
|
|
|
15
0
如果其他人也有类似的问题,据我所知,以下代码可以工作:
在OP的情况下,它的用法如下:
如果同步代码不应返回任何内容,则可以这样编写同步方法:
|
|
|
16
0
我添加了一个小的锁类,可以锁定/同步任何键,包括字符串。 参见Java 8、Java 6和小测试的实现。 Java 8:
Java 6: 公共类dynamickelock实现锁 { private final static concurrenthashmap locksmap=new concurrenthashmap(); 专用最终T键;
测试:
|
|
|
BenjiFB · C#内存缓存:在一次操作中追加到列表? 1 年前 |
|
SpeakX · 多线程上下文中的弱指针未过期时更新 2 年前 |
|
USMC6072 · 在父事件处理程序中调用异步方法会引发错误 3 年前 |
|
|
Eric · fgets_unlocked()用于多线程读取不同文件 4 年前 |