双重调用WeakHashMap.put(..)背后的原因是什么?

时间:2009-05-05 08:39:13

标签: java multithreading concurrency mutex

这个blog post演示了一种实现每个字符串id惯用法的互斥的方法。使用的字符串ID表示HttpSession ID。

  1. 为什么我们需要在Mutex实例周围包装WeakReference?从字符串创建Map是不是更好 - > Mutex?
  2. 为什么我们需要两次调用put?

    public Mutex getMutex( String id )
    {                                  
        Mutex key = new MutexImpl( id );
        synchronized( mutexMap )
        {
            WeakReference<Mutex> ref = mutexMap.get( key );
            if( ref == null )
            {
                mutexMap.put( key, new WeakReference<Mutex>( key ) );
                return key;
            }
            Mutex mutex = ref.get();
            if( mutex == null )
            {
                mutexMap.put( key, new WeakReference<Mutex>( key ) );
                return key;
            }
            return mutex;
        }
    }
    

4 个答案:

答案 0 :(得分:4)

WeakHashMap中的值对象由普通的强引用保存。因此,应该注意确保值对象不直接或间接地强烈引用它们自己的密钥,因为这将防止密钥被丢弃。注意,值对象可以通过WeakHashMap本身间接引用它的键;也就是说,值对象可以强烈地引用一些其他关键对象,其关联值对象又强烈地引用第一值对象的关键字。处理此问题的一种方法是在插入之前将值本身包装在WeakReferences中,如:m.put(key,new WeakReference(value)),然后在每次get时解包。

答案 1 :(得分:2)

1 - @Loop有good answer

2 - 假设条目包含WeakReferences,第二个put是必需的,因为在执行到达行之前可能会收集WeakReference

 Mutex mutex = ref.get();

在这种情况下:

  • 该条目可能尚未存在于map
  • 如果存在,可以在执行Mutex mutex = ref.get();
  • 之前收集

答案 2 :(得分:1)

我会说WeakReference只是因为需要引用http会话id而使用。您无法始终100%确定您收到会话结束的通知,从而导致地图不断增长。

第一次拨打电话是因为地图不包含您的密钥。第二次是因为地图确实包含了密钥,但引用不再存在。

答案 3 :(得分:1)

LoopBruno Conde几乎涵盖了它,但自从我写了这段代码......

设计目标是避免让用户调用释放机制 - 当用户不再引用它时,互斥锁有资格进行垃圾收集。

  

为什么我们需要包装WeakReference   在Mutex实例周围?

地图是WeakHashMap

private final Map mutexMap = new WeakHashMap();

此映射用于保留对互斥锁的引用,但如果对键和值使用相同的对象,则该对象不符合垃圾回收的条件。的Javadoc:

  

实施说明:价值对象   在WeakHashMap中是普通的   强烈的参考。因此应该谨慎   采取以确保价值对象   不强烈提及自己的钥匙,   直接或间接地,因为   这将阻止密钥   丢弃。注意一个值对象   可以间接引用其关键通道   WeakHashMap本身;这是一个   价值对象可能强烈提及   一些其他关键对象   反过来,价值对象强烈地指   到第一个值对象的键。   解决这个问题的一种方法是包装   重视自己   插入前的WeakReferences,如   in:m.put(key,new   WeakReference(value)),然后   解开每一个。


  

不是吗   最好只创建一个Map   字符串 - &gt;互斥?

该String值何时被垃圾收集?是否每次都传递相同的引用?是否intern()被调用了吗?如果我打电话给实习生,那么String会活多久?如果String是键,则在不需要保留对它的引用之后,互斥锁可能没有资格进行垃圾收集。


  

为什么我们需要两次调用put?

有两种情况需要处理,直到该方法能够在地图中获得对互斥锁的强引用:

  • WeakReference已被垃圾收集(或者从未出现在那里)
  • 在获取对它的引用之后,WeakReference的内容被垃圾收集

put 只会被调用一次;该方法在之后立即返回。

(WeakReference可以在第二次使用中重复使用,但我不认为这将是一个重大改进。)


当然,如果有人可以在代码中找到错误,请告诉我,我会很乐意纠正它。此外,单元测试尝试检查实现是否泄漏,因此可以随意修改代码并查看运行测试时会发生什么。