我试图找出这段代码中是否会出现任何竞争条件。如果密钥不是'Thread.currentThread'那么我会认为是的。但由于线程本身是关键,如何才能有竞争条件?没有其他线程可以在HashMap中更新相同的密钥!
public class SessionTracker {
static private final Map<Thread,Session> threadSessionMap = new HashMap<Thread,Session>();
static public Session get() {
return threadSessionMap.get(Thread.currentThread());
}
static public void set(Session s) {
threadSessionMap.put(Thread.currentThread(),s);
}
static public void reset() {
threadSessionMap.remove(Thread.currentThread());
}
}
答案 0 :(得分:13)
答案是肯定的,有潜在的竞争条件:
为了更好地解释我在第二点上的意思,我正在查看source code of HashMap in OpenJdk 7
389 int hash = hash(key.hashCode());
390 int i = indexFor(hash, table.length);
首先计算密钥的哈希值(组合两个哈希函数),然后映射到具有indexFor
的单元格,然后检查该单元格是否包含相同的密钥或已被另一个密钥占用。如果它是相同的键,它只是覆盖该值,这里没有问题。
如果它被占用它会查看下一个单元格,然后查看下一个单元格,直到它找到一个空位置并调用addEntry()
,如果数组比某个{{1}更加负载,它甚至可以决定调整数组的大小}}。
我们包含条目的loadFactor
只是table
的向量,它包含键和值。
Entry
在并发环境中,所有类型的恶意事件都可能发生,例如,一个线程获取5号单元格的冲突并查找下一个单元格(6)并将其查找为空。
同时另一个线程因146 /**
147 * The table, resized as necessary. Length MUST Always be a power of two.
148 */
149 transient Entry[] table;
而获得索引6,并且两者都决定同时使用该单元格,其中一个会覆盖另一个。
答案 1 :(得分:2)
在没有深入了解Hashmap实现的具体细节的情况下,我会说存在错误的可能性,因为Hashmap类对于并发访问是不安全的。
虽然我同意一次只对单个密钥进行一次修改,因为您使用的是currentThread(),但仍有可能多个线程同时修改Hashmap 。除非你看一下具体的实现,否则你不应该假设只有对同一个密钥的并发访问会导致Hashmap出现问题,并且不会对不同的密钥进行并发修改。
想象一下两个不同的密钥生成相同的哈希值的情况,并且很容易看出并发修改时仍然存在错误。
答案 2 :(得分:1)
是的,这不是一件安全的事情(正如其他答案已经指出的那样)。一个更好的解决方案可能是使用ThreadLocal,这是一种比使用Map更自然的方式来保存线程本地数据。它有一些很好的功能,包括默认值,并在线程终止时删除这些值。
答案 3 :(得分:0)
根据Pierre Hugues编写的article,如果你在多个线程之间共享hashmap,你的进程可能因为无限循环而挂起并占用你所有的cpu资源。
答案 4 :(得分:0)
我同意以前的答案,即您的代码不是线程安全的,而使用ConcurrentHashMap可以解决您的问题,这是ThreadLocal的完美用例。
ThreadLocal的简短介绍:
ThreadLocal将在内部为访问ThreadLocal的每个线程保存一个类的不同实例,从而解决任何并发问题。另外(取决于情况可能是好/坏),存储在ThreadLocal中的值只能由首先填充该值的线程访问。如果这是当前线程第一次访问ThreadLocal,则该值将为null。
包含String值的ThreadLocal的简单示例:
private static ThreadLocal<String> threadVar = new ThreadLocal<>();
public void foo() {
String myString = threadVar.get();
if (myString == null) {
threadVar.set("some new value");
myString = threadVar.get();
}
}