面试题答案
一键面试1. Hashtable在多线程高并发写入场景的问题
- 性能问题:Hashtable内部使用
synchronized
关键字修饰方法,这意味着同一时刻只有一个线程能访问Hashtable的任何方法,在高并发写入场景下,大量线程会被阻塞,导致性能低下。 - 死锁风险:在复杂的多线程环境中,如果多个线程对Hashtable进行嵌套同步访问,可能会导致死锁。
2. 优化措施及代码示例
使用ConcurrentHashMap替换Hashtable
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
private static ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
map.put("key" + i, i);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 1000; i < 2000; i++) {
map.put("key" + i, i);
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Map size: " + map.size());
}
}
ConcurrentHashMap采用了更加细粒度的锁机制(例如分段锁或基于CAS操作),允许多个线程同时访问不同的段,大大提高了并发性能。
使用Collections.synchronizedMap并指定合适的锁粒度(如果必须使用类似Hashtable的同步行为)
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class SynchronizedMapExample {
private static Map<String, Integer> synchronizedMap;
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
synchronizedMap = Collections.synchronizedMap(map);
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
synchronized (synchronizedMap) {
synchronizedMap.put("key" + i, i);
}
}
});
Thread thread2 = new Thread(() -> {
for (int i = 1000; i < 2000; i++) {
synchronized (synchronizedMap) {
synchronizedMap.put("key" + i, i);
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Map size: " + synchronizedMap.size());
}
}
这里通过Collections.synchronizedMap
创建一个同步的Map,并且手动控制同步块,缩小锁的粒度,减少线程竞争,从而在一定程度上提高并发性能。但这种方式相较于ConcurrentHashMap,性能还是会低一些,因为整体的同步粒度还是相对较粗。