1.HashMap在多线程情况下,可能出现问题的环境有哪些?
HashMap是数组+单向链表/红黑树的结构,可能出现问题。
①在对一个数组位置上为空新增一个节点时,若多线程同时tab[i] = new Node(),则其他线程操作的数据将会丢失。
2.ConcurrentHashMap是如何解决上面的问题的?
①针对数据位置上为空,多线程同时新增的问题,采用CAS(乐观锁)的方式新增元素,若失败,则通过外部的死循环再次进行新增,此时就会追加在链表上。
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
//若CAS失败,则再次循环,就将走下面省略的部分代码
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
....
3.ArrayList的线程不安全是在哪处代码上?
在新增操作中,会通过size++的方式获取数组下标,而这种方式本身就是线程不安全的,可能出现数据被清除或空元素的情况。
elementData[size++] = e;
4.CopyOnWriteArrayList是如何解决上面的问题的?
是使用ReentrantLock加锁的方式解决。
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//需要注意的是,每次新增都会创建一个新的数组,因此效率很低
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
5.JVM内存结构和java内存模型的区别
JVM内存结构也就是 堆、栈、元空间(方法区)、程序计数器等。
java内存模型(JMM)是指多线程的通信问题,涉及工作内存和主内存、共享变量和共享变量副本、volatile和synchronized。
volatile修饰的变量,在多线程操作情况下,线程可以及时获取变量的最新值,但是对于获取变量后再进行赋值操作,在多线程情况下依旧可能出现数据问题。这是因为volatile只能保证加载变量值从主内存获取,但是获取到之后的赋值是在工作内存中进行,并会写到主内存中的。因此当多个线程已经从主内存加载到值后,再进行操作不能保证数据安全。
依然还是需要通过synchronized加锁方式解决。
6.以下变量的等值比较结果
Integer i1 = 1;
Integer i2 = new Integer(1);
Integer i3 = Integer.valueOf(1);
System.out.println(i1 == i2);
System.out.println(i1 == i3);
System.out.println(i2 == i3);
结果是:
false
true
false
通过valueOf()或自动装箱方式定义的Byte/Short/Integer/Long对象,在-128~127之间时,会从缓存中获取,而通过new Integer(1)方式定义的则是新建的对象。
若这3个变量都是130,那么结果就将都是false,因为自动装箱也就是valueOf()对于超出范围的,直接新建对象。
Integer i1 = 130;
Integer i1_2 = 130;
Integer i2 = new Integer(130);
Integer i3 = Integer.valueOf(130);
System.out.println(i1 == i1_2);
System.out.println(i1 == i2);
System.out.println(i1 == i3);
System.out.println(i2 == i3);
结果:
false
false
false
false