简体   繁体   English

奇怪的HashMap.put()行为

[英]strange HashMap.put() behaviour

I am attempting to troubleshoot an intermittent failure that appears to be related to removing an object from a HashMap and then putting that same object back using a new key. 我正在尝试解决间歇性故障,该间歇性故障似乎与从HashMap中删除对象有关,然后使用新密钥将同一对象放回原处。 My HashMap is created as follows: 我的HashMap创建如下:

transactions = new HashMap<Short, TransactionBase>();

The code that does the re-assignment is as follows: 重新分配的代码如下:

transactions.remove(transaction.tran_no);
transaction.tran_no = generate_transaction_id();
transactions.put(transaction.tran_no, transaction);

The intermittent behaviour that I am seeing is that code that executes immediately after this that depends upon the transaction object being locatable does not appear to find the transaction object using the new transaction id. 我看到的间歇性行为是,此后立即执行的代码依赖于可定位的交易对象而似乎立即执行,因此似乎无法使用新的交易ID找到交易对象。 However, at some future point, the transaction can be located. 但是,可以在将来的某个时间找到交易。 So pulling at straws, is there any sort of asynchronous effect to put() or remove that can cause this sort of behaviour? 因此,请放心,put()或remove有任何异步效应会导致这种行为吗?

I should mention that, to the best of my knowledge, the container is being accessed by only one thread. 我应该说,就我所知,该容器仅由一个线程访问。 I have already read in he documentation that class HashMap is not "synchronised". 我已经在他的文档中阅读到HashMap类不是“同步的”。

There is a slight difference between remove/get and put (although my guess is that you have a threading issue). 在remove / get和put之间有细微的差别(尽管我猜是您有线程问题)。

The parameter for remove / get is of type Object ; remove / get的参数为Object类型; for put it is of type K . put它的类型K The reason for that has been stated many times before. 其原因已被多次陈述。 This means it has problems with boxing. 这意味着拳击有问题。 I'm not even going to guess what the rules are. 我什至不会猜测规则是什么。 If a value gets boxed as a Byte in one place and a Short in another, then those two objects cannot be equal. 如果一个值在一个位置被包装为一个Byte ,而在另一个位置被包装为一个Short ,则这两个对象不能相等。

There is a similar issue with List.remove(int) and List.remove(Object) . List.remove(int)List.remove(Object)也存在类似问题。

I presume that every time you check for the presence of the item you're definitely using a short or Short argument to Map.get() or Map.contains() ? 我想每次您检查项目是否存在时,您肯定是对Map.get()Map.contains()使用shortShort argument吗?

These methods take Object arguments so if you pass them an int it will be converted to an Integer and will never match any item in your Map because they will all have Short keys. 这些方法采用Object参数,因此,如果将它们传递给int则它将被转换为Integer并且将永远不会与Map中的任何项目匹配,因为它们都具有Short键。

There are no "asynchronous" effects in the HashMap class. HashMap类中没有“异步”效果。 As soon as you put something in there, it's there. 只要在其中放东西,它就在那里。 You should double- and triple- check to make sure that there are no threading issues. 您应该进行两次和三次检查,以确保没有线程问题。

The only other thing I can think of is that you're making a copy of the HashMap somewhere. 我唯一想到的另一件事是您正在某个地方复制HashMap。 The copy obviously won't be affected by you adding stuff into the original, or vice versa. 您在原件中添加内容显然不会影响该副本,反之亦然。

只是一个建议...您正在关注对HashMap的访问,但是我想知道您是否还应该查看您的generate_transaction_id()是否是线程安全的,或者它是否以异常方式运行。

Have you overridden equals() but not hashCode() in your model objects? 您是否在模型对象中覆盖了equals()而不是hashCode() How about compareTo() ? compareTo()呢? If you get these wrong, Collections will behave strangely indeed. 如果您弄错了这些,Collections的确会表现得很奇怪。

Check Java Practices on equals() and compareTo() . equals()compareTo()上检查Java实践。

So you know HashMap 's not thread safe. 因此,您知道HashMap不是线程安全的。 Are you sure it's being accessed by only one thread? 您确定只有一个线程可以访问它吗? After all, intermittent failures a frequently threading related. 毕竟,间歇性故障通常与线程相关。 If not, you can wrap it with Collections.synchronizedMap() , like so: 如果没有,则可以使用Collections.synchronizedMap()将其包装起来,如下所示:

Collections.synchronizedMap(transactions);

You could always just try so you can eliminate that possibility. 您总是可以尝试,以便消除这种可能性。

It should be pointed out that this just wraps the original map with one with all the methods synchronized. 应该指出的是,这只是将原始地图包装在一起,并且所有方法都同步了。 You may want to consider using a synchronized block if the access is localized. 如果访问已本地化,则可能要考虑使用同步块。

What does this generate_transaction_id() function do? 这个generate_transaction_id()函数做什么? If it is generating a 16-bit GUID-like thing, you could easily get hash collisions. 如果它生成的是类似16位GUID的东西,则很容易出现哈希冲突。 Combined with threading, you could get: 结合线程,您可以获得:

T1: transaction1.tran_no = generate_transaction_id();    => 1729
T2: transaction2.tran_no = generate_transaction_id();    => 1729
T1: transactions.put(transaction1.tran_no, transaction1); => map.put(1729, transaction1)
T2: transactions.put(transaction2.tran_no, transaction2); => map.put(1729, transaction2)
T1: int tran_no = transactions.get(1729);               => transaction2
T1: transactions.remove(transaction.tran_no);           => map.remove(1729)
T2: int tran_no = transactions.get(1729);               => null

Of course, this could only be a solution if that 'to the best of your knowledge' part is not true. 当然,仅在“据您所知”部分不成立的情况下,这才是解决方案。

As others have observed you HAVE to know whether the HashMap is accessed by just one Thread, or not. 正如其他人观察到的那样,您必须知道HashMap是否仅由一个线程访问。 CollectionSpy is a new profiler that lets you find out instantly, for all containers, how many threads perform any accesses. CollectionSpy是一个新的探查器,可让您立即查找所有容器中执行任何访问的线程数。 See www.collectionspy.com for more details. 有关更多详细信息,请参见www.collectionspy.com

Threading has been mentioned in a few responses already, but have you considered the visibility issue for objects used by multiple threads? 已经在一些响应中提到了线程,但是您是否考虑过多个线程使用的对象的可见性问题? It's possible (and quite common) that if you put an object into a collection in one thread, it will not be "published" to other threads unless you have properly synchronized on the collection. 如果将对象放在一个线程中的集合中,则可能(而且很常见),除非您对该集合进行了适当的同步,否则该对象将不会“发布”到其他线程。

Threads and Locks 螺纹和锁

Synchronization and thread safety in Java Java中的同步和线程安全

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM