线程安全。 换成线程安全map。public static ConcurrentMap<Thread, Integer> map = new ConcurrentHashMap<Thread, Integer>();
由于HashMap是非线程安全的,两个线程同时写入值时,可能相互覆盖。 报错中的空指针,是由于从map中取到的value为null,在自动解箱转为int时,抛出了空指针异常。当两个线程对象的hash值同时分配到map的同一个桶时,如下代码如果同时执行,只会保留后一个线程的值。 测试的结果也验证了分析,报错时,hash后的值在同一个桶中。 /** * Adds a new entry with the specified key, value and hash code to the * specified bucket. It is the responsibility of this method to resize the * table if appropriate. * * Subclass overrides this to alter the behavior of put method. */ void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K, V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K, V>(hash, key, value, e);//同时执行,后执行的线程覆盖前面写入的 if (size++ >= threshold) resize(2 * table.length); }package net.jcip.test;import java.util.HashMap; import java.util.Map; import java.util.Random;public class ThreadScopeShareData { private static Map<Thread, Integer> threadMap = new HashMap<Thread, Integer>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { int data = new Random().nextInt(); System.out.println(indexFor(hash(Thread.currentThread() .hashCode()), 16)); System.out.println(Thread.currentThread().getName() + " has put data: " + data); threadMap.put(Thread.currentThread(), data); new A().get(); new B().get(); } }).start(); } } static class A { public void get() { System.out.println(Thread.currentThread() + " threadMap= " + threadMap); int data = threadMap.get(Thread.currentThread()); System.out.println("from A " + Thread.currentThread().getName() + " get data: " + data); } } static class B { public void get() { int data = threadMap.get(Thread.currentThread()); System.out.println("from B " + Thread.currentThread().getName() + " get data: " + data); } } static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } /** * Returns index for hash code h. */ static int indexFor(int h, int length) { return h & (length - 1); } } 14 Thread-1 has put data: 1593710152 14 Thread-0 has put data: 1594094901 Thread[Thread-0,5,main] threadMap= {Thread[Thread-0,5,main]=1594094901} from A Thread-0 get data: 1594094901 Thread[Thread-1,5,main] threadMap= {Thread[Thread-0,5,main]=1594094901} from B Thread-0 get data: 1594094901 Exception in thread "Thread-1" java.lang.NullPointerException at net.jcip.test.ThreadScopeShareData$A.get(ThreadScopeShareData.java:34) at net.jcip.test.ThreadScopeShareData$1.run(ThreadScopeShareData.java:21) at java.lang.Thread.run(Unknown Source)
换成线程安全map。public static ConcurrentMap<Thread, Integer> map = new ConcurrentHashMap<Thread, Integer>();
报错中的空指针,是由于从map中取到的value为null,在自动解箱转为int时,抛出了空指针异常。当两个线程对象的hash值同时分配到map的同一个桶时,如下代码如果同时执行,只会保留后一个线程的值。
测试的结果也验证了分析,报错时,hash后的值在同一个桶中。
/**
* Adds a new entry with the specified key, value and hash code to the
* specified bucket. It is the responsibility of this method to resize the
* table if appropriate.
*
* Subclass overrides this to alter the behavior of put method.
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K, V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K, V>(hash, key, value, e);//同时执行,后执行的线程覆盖前面写入的
if (size++ >= threshold)
resize(2 * table.length);
}package net.jcip.test;import java.util.HashMap;
import java.util.Map;
import java.util.Random;public class ThreadScopeShareData {
private static Map<Thread, Integer> threadMap = new HashMap<Thread, Integer>(); public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(indexFor(hash(Thread.currentThread()
.hashCode()), 16));
System.out.println(Thread.currentThread().getName()
+ " has put data: " + data);
threadMap.put(Thread.currentThread(), data);
new A().get();
new B().get();
} }).start();
} } static class A {
public void get() {
System.out.println(Thread.currentThread() + " threadMap= "
+ threadMap);
int data = threadMap.get(Thread.currentThread());
System.out.println("from A " + Thread.currentThread().getName()
+ " get data: " + data);
}
} static class B {
public void get() {
int data = threadMap.get(Thread.currentThread());
System.out.println("from B " + Thread.currentThread().getName()
+ " get data: " + data);
}
} static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
} /**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length - 1);
}
}
14
Thread-1 has put data: 1593710152
14
Thread-0 has put data: 1594094901
Thread[Thread-0,5,main] threadMap= {Thread[Thread-0,5,main]=1594094901}
from A Thread-0 get data: 1594094901
Thread[Thread-1,5,main] threadMap= {Thread[Thread-0,5,main]=1594094901}
from B Thread-0 get data: 1594094901
Exception in thread "Thread-1" java.lang.NullPointerException
at net.jcip.test.ThreadScopeShareData$A.get(ThreadScopeShareData.java:34)
at net.jcip.test.ThreadScopeShareData$1.run(ThreadScopeShareData.java:21)
at java.lang.Thread.run(Unknown Source)