2

这篇博客文章演示了一种为每个字符串 id 惯用语实现互斥锁的方法。使用的 String id 用于表示 HttpSession id。

  1. 为什么我们需要在 Mutex 实例周围包裹一个 WeakReference ?从 String -> Mutex 创建一个 Map 不是更好吗?
  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

4 回答 4

4

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

于 2009-05-05T09:09:18.743 回答
2

1 - @Loop 有一个很好的答案

2 - 假设条目用 Wea​​kReferences 包装,第二个put是必要的,因为它WeakReference可能在执行到达行之前收集:

 Mutex mutex = ref.get();

在这种情况下:

  • 该条目可能不存在于map
  • 如果存在,可以在执行前收集Mutex mutex = ref.get();
于 2009-05-05T09:12:32.263 回答
1

LoopBruno Conde几乎涵盖了它,但自从我编写了那个代码......

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

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

该地图是WeakHashMap

private final Map mutexMap = new WeakHashMap();

此映射用于保持对互斥体的引用,但如果您对键和值使用相同的对象,则该对象不符合垃圾回收条件。Java 文档:

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


从 String -> Mutex 创建一个 Map 不是更好吗?

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


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

有两种情况需要处理,直到该方法可以在映射中获得对互斥体的强引用:

  • WeakReference已被垃圾收集(或一开始就没有)
  • WeakReference 的内容在获得对它的引用后被垃圾收集

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

(WeakReference 可以在第二个 put 中重用,但我认为这不会是一个显着的改进。)


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

于 2009-05-05T09:31:39.533 回答
1

我会说使用WeakReference's 只是因为它们需要引用 http 会话 ID。您不能总是 100% 确定您会收到会话结束的通知,这样会导致地图不断增长。

您第一次调用 put 是因为地图不包含您的密钥。第二次是因为地图确实包含密钥但引用不再存在。

于 2009-05-05T09:10:20.193 回答