互斥锁与对象问题

3
我正在查看Collections.synhronizedMap()的Java代码。我发现它有以下实现方式。现在我的问题是: 1)为什么我们要使用synchronized(mutex) 2)如果我们使用synchronized(m)会怎样 我的理解是,如果我们使用synchronized(m),那么仍然只有一个线程能够访问该映射表。
static class SynchronizedMap<K, V> implements Map<K, V>, Serializable {
    private static final long serialVersionUID = 1978198479659022715L;

    private final Map<K, V> m;

    final Object mutex;

    SynchronizedMap(Map<K, V> map) {
        m = map;
        mutex = this;
    }

    SynchronizedMap(Map<K, V> map, Object mutex) {
        m = map;
        this.mutex = mutex;
    }

    public void clear() {
        synchronized (mutex) {
            m.clear();
        }
    }

    public boolean containsKey(Object key) {
        synchronized (mutex) {
            return m.containsKey(key);
        }
    }

    public boolean containsValue(Object value) {
        synchronized (mutex) {
            return m.containsValue(value);
        }
    }

    public Set<Map.Entry<K, V>> entrySet() {
        synchronized (mutex) {
            return new SynchronizedSet<Map.Entry<K, V>>(m.entrySet(), mutex);
        }
    }

    @Override
    public boolean equals(Object object) {
        synchronized (mutex) {
            return m.equals(object);
        }
    }

编辑:我希望有一些澄清,这也与此问题相关

1)Java 的this关键字用于引用其所在方法的当前实例。因此,如果我在此关键字上放置互斥锁,这是否意味着我们将锁定对象的当前实例,还是将锁定线程的当前实例?如果有人能够更全面地解释以下语句的含义,我将不胜感激。

mutex = this;
2个回答

4
无论哪种情况,每次只能有一个线程访问映射内容。
最好的方式是将互斥锁与映射分开。如果传入的映射正在由其他线程进行同步,则不会影响此对象。将锁与映射分开可以让用户单独指定锁,因此允许用户与其他东西共享锁。
这个类被编写成可以从外部使用锁,或者将自身作为锁使用。为什么不明显,因为没有给出上下文,我假设在需要跨此映射和其他某些东西进行粗粒度锁定的情况下可能存在这种情况。
我更喜欢一些类似的东西
private final Object mutex;

public SynchronizedMap(Map<K,V> map, Object mutex) {
    m = map;
    this.mutex = mutex;
}

public SynchronizedMap(Map<K,V> map) {
    this(map, new Object());
}

这样做至少有一个选择,即让对象独占锁定。

this在此处指的是SynchronizedMap对象。

顺便说一下,可能它不使用私有锁,以便支持客户端锁定。


你能详细描述一下吗?我在其他课程中也看到过这个。最后,ReentrantLock锁定器是这个类的实例。您能否向我推荐任何阅读材料,以便我可以完全理解它。请考虑我是一个对此不太熟悉的人。这将有助于我和其他试图理解当前并发情况的人们。 - Anil Sharma
@Anil:我不知道你需要什么详细信息,你可以让你的问题更具体一些。 - Nathan Hughes
很抱歉,Java 的这些代码不需要那么多的批评。它是 java.util.Collections 类的一部分。基本上,我正在查看 Collections.synchronizedMap() 方法的同步处理。实际上,我并不想粘贴超过 1000 行的整个类,所以只复制了那部分代码。 - Anil Sharma
@Anil:这是当前对象,而不是当前线程。这里没有锁定任何线程。 - Nathan Hughes
@Magnamag,我并不是说整体设计有什么问题;我是在说如果你把SynchronizedMap(如上所示)从上下文中剥离出来,它就不是一个好的设计示例。没有上下文,这个类的私有字段似乎引用了一个可变对象,这个对象可能会被其他类中的代码修改。通常情况下,这是不好的设计,但如果这个类被隐藏在一个模块中,并且永远不允许以这种特定的方式使用它,那么这可能是可以接受的。 - Solomon Slow
显示剩余4条评论

2
作为其他答案中已经指出的一样:通常情况下,建议使用明确、专用、私有的“锁”对象,而不是通过公共方法暴露同步(在后一种情况下,“对象本身”将成为锁,在某些情况下这可能很容易导致死锁)。
然而,在这里情况有所不同,因为所有这些都涉及到Collections类的一个私有内部类。特别的,存在一个显式的mutex参数的主要原因是,这个互斥体可以在多个实例之间共享。构造函数
SynchronizedMap(Map<K,V> m, Object mutex)

该函数(仅!)在子类中调用,即在

SynchronizedSortedMap(SortedMap<K,V> m, Object mutex) {
    super(m, mutex);
    sm = m;
}

这个构造函数只是将给定的互斥锁传递到SynchronizedMap中。而这个构造函数,则被调用于SynchronizedSortedSet类的subMaptailMapheadMap方法中。例如:

public SortedMap<K,V> subMap(K fromKey, K toKey) {
    synchronized (mutex) {
        return new SynchronizedSortedMap<>(
            sm.subMap(fromKey, toKey), mutex);
    }
}

在这里,实际地图的互斥锁也被传递给子地图。因此,这里的效果是使用相同的互斥锁用于地图及其子地图。如果在SynchronizedMap中的同步是使用synchronized(m)与委托地图同步,那么这将是不可能的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接