Java 客户机/服务器多线程和ConcurrentHashMap-为什么是isn';t客户端。是否锁定(id)?

Java 客户机/服务器多线程和ConcurrentHashMap-为什么是isn';t客户端。是否锁定(id)?,java,multithreading,synchronized,concurrenthashmap,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 = n

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

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)
没有被锁定有什么特殊原因吗?

很难理解你想要实现什么,
clients.get(id)
返回一个对象的实例,你正在同步它

好的

这不会阻止另一个线程访问并发hashmap。我怀疑您想阻止对Hashmap的访问,在这种情况下,您应该在if语句的两个分支中使用Object()作为互斥对象,因为您正在将一个新对象放入映射中。因此,作为client.get(id)的结果,所有其他线程将找到一个不同的对象。即使贴图中有两个相等的整数,它们也不是相同的对象。 示例:如果“o”始终是一个字符串,则每次执行代码都会替换映射中的值,接下来的每个线程都会在client.get()中获得一个新对象(有些线程可能很幸运地获得了上一个对象,但大多数线程都会获得一个新对象,因为与流处理和睡眠相比,同步块非常小且速度很快(因为对象在睡眠结束之前被替换)

如果您想在一个不存在的对象上,在对象的想法(或id)上进行同步,请查看这个github repo:

两件事:第一件事

不应使用新整数,而应使用新整数

另一件事是,甚至不需要使用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();
其中,
客户机
定义为(将字符串替换为您使用的内容):

最终ConcurrentHashMap客户端;
这使用了与其他原子中相同的概念:它会重复,直到线程之间没有干扰。最大的好处是不需要阻塞线程,因此在多核机器上,所有内核都可以始终以100%的速度使用


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

每个调用线程可能在不同的对象上同步,因为“id”将具有不同的值,因此返回不同的对象。您应该只在同一个对象上同步。我的测试使用单个id轰炸我的服务器,然后使用两个,然后是三个,然后是10个,每个id总共进行100个连接。每次id数增加一倍,所需时间应该是前一次的一半。我已经测试过这个,这显然没有发生。你有一个客户。获取(id)它将返回0或另一个int值,并且您正在锁定此整数值。不确定您打算从中获得什么,但它不会锁定影响同步块内代码的任何内容。很难理解您试图实现什么,
cleints.get(id)
返回一个对象的实例,您正在该实例上进行同步。很好。这不会阻止另一个线程访问并发hashmap。我怀疑您想阻止访问hashmap,您应该在其中使用Object()作为互斥体。
final ConcurrentHashMap<String, Integer> clients;