1.Hash扩容算法问题:
a.多线程put操作,get会死循环,这个可以优化掉比如扩容的时候新开一个数组,不要使用共享的那个数组
b.多线程put可能导致get取值错误
问题分析:
hash冲突一般是采用链式结构来保存冲突的值,如果在遍历这个链表时,它本身是这样的 1->2->3->4->null
遍历到3本身应该是Null的,这时候刚好有人把这个null给计算出了值,null=>1->3,这下就完了,原来3要指向null结束的,
这下又编程指向了1,而这个1又刚好指向3,这就出现了死循环问题。
解决方案:
a.单线程操作
b.多线程场景要注意加锁,比如jdk里可以使用ConCurrentHashMap 分段锁等等。
2.Random随机数重复问题
Random类产生随机数是根据“种子数”进行一系列算法得出的,并发场景下,若种子数相同,那得出的随机数结果也必然相同。
问题分析:
观察Random类,发现其中提供了两个构造方法,一个有参一个无参,而无参方法其实就是传入一个nanoTime作为Random的种子数 “seed”:
有参方法:
public Random(long seed) {
if (getClass() == Random.class)
this.seed = new AtomicLong(initialScramble(seed));
else {
// subclass might have overriden setSeed
this.seed = new AtomicLong();
setSeed(seed);
}
}
//无参方法
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
System.nanoTime()方法,会返回正在运行的Java虚拟机的高分辨率时间源的当前值,以纳秒为单位。
接下来看关键地方:
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
这是 random.nextInt() 调用的底层方法,while 的判断条件是:当旧种子数oldseed和新种子数nextseed不同时,由新种子nextseed计算得出随机数。
可以看到,新种子数nextseed =
(旧种子数oldseed * multiplier(定值) + addend(定值))& mask(定值)
那么在高并发场景下,多个线程在同一纳秒级别调用了Random,或者多个线程同时访问同一个Random方法来产生的随机数,由于oldseed相同,所计算出的随机值那也是相同的。
解决方案:
a.单线程操作
b. 使用线程安全类 ThreadLocalRandom.current().nextInt(10);