客户端/服务器多线程和ConcurrentHashMap - 为什么不锁定clients.get(id)?

时间:2014-01-30 04:50:21

标签: java multithreading synchronized concurrenthashmap

我的服务器中有以下代码块:

clients.putIfAbsent(id, new Integer(0));
synchronized (clients.get(id)) {
    if (o instanceof Integer) {
        x = new Integer(((Integer) o).intValue());
        value = clients.get(id); // existing value in HashMap
        value = new Integer(value.intValue() + x);
        clients.put(id, value);
    } else if (o instanceof String) {
        clients.put(id, new Integer(0));
    }
    Thread.sleep(SockServer5.sleepTime);
out.writeObject(clients.get(id));
    out.flush();
}

clients是ConcurrentHashMap,而o是从客户端读取的对象输入。对于每个新的客户端连接,服务器会生成一个新线程。

我想知道,我的clients.get(id)没有被锁定是否有任何特殊原因?

3 个答案:

答案 0 :(得分:0)

很难理解你想要实现的目标,clients.get(id)返回一个Object的实例并且你正在同步它。

精细。

这不会阻止另一个线程访问并发hashmap。我怀疑你想阻止访问Hashmap,在这种情况下你应该使用Object()作为互斥体

答案 1 :(得分:0)

if 语句的两个分支中,您将新对象放入地图中。因此,所有其他线程将找到client.get(id)的结果的不同对象。即使地图中有两个相等的整数,它们也不是同一个对象。 示例:如果'o'始终是一个字符串,则每次执行代码都将替换map中的值,并且每个后续线程将在client.get()中获取一个新对象(有些人可能很幸运能够得到一个对象,但大多数会得到一个新的,因为与流处理和你的睡眠相比,synchronized块相当小而且快(因为在睡眠结束之前对象被替换))。

如果你想在一个不存在的对象上同步对象的想法(或id),请查看这个github repo:https://github.com/anotheria/idbasedlock

答案 2 :(得分:0)

两件事:第一件事

  

不能使用新的整数但Integer.valueOf(i)

另一件事是甚至不需要使用synchronized。它完全违背了使用并发映射的原因,因为由于阻塞,它将线程限制为单线程。这是一个不使用synchronized但仍然是线程安全的解决方案:

clients.putIfAbsent(id, Integer.valueOf(0));

Integer value;
do {
  value = clients.get(id);
} while (clients.replace(id, value, value + o) == false); // repeat until value did not change in between

Thread.sleep(SockServer5.sleepTime);
out.writeObject(value);
out.flush();

clients定义为(用您使用的任何内容替换String):

final ConcurrentHashMap<String, Integer> clients;

这与其他Atomics中的compareAndSet使用相同的概念:它重复直到没有线程干扰。最大的好处是不需要阻塞任何线程,因此在多核机器上,所有核心都可以100%一直使用

请注意value + o中自动装箱整数的用法。 Java将自己使用最有效的表单。