一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情。
为什么要使用ConcurrentHashMap:
- HashMap线程不安全,会导致数据错乱
- 使用线程安全的Hashtable效率低下
基于以上两个原因,便有了ConcurrentHashMap的登场机会。
- HashMap线程不安全演示。
公有、静态的集合:
public class Const {
public static HashMap<String,String> map = new HashMap<>();
}
线程,向map中写入数据:
public void run() {
for (int i = 0; i < 500000; i++) {
Const.map.put(this.getName() + (i + 1), this.getName() + i + 1);
}
System.out.println(this.getName() + " 结束!");
}
测试类:
public class Demo {
public static void main(String[] args) throws InterruptedException {
Thread1A a1 = new Thread1A();
Thread1A a2 = new Thread1A();
a1.setName("线程1-");
a2.setName("线程2-");
a1.start();
a2.start();
//休息10秒,确保两个线程执行完毕
Thread.sleep(1000 * 5);
//打印集合大小
System.out.println("Map大小:" + Const.map.size());
}
}
说明:两个线程分别向同一个map中写入50000个键值对,最后map的size应为:100000,但多运行几次会发现有以下几种错误:
- 假死:
- 异常:
- 错误结果:
为了保证线程安全,可以使用Hashtable。注意:线程中加入了计时
公有、静态的集合:
public class Const {
public static Hashtable<String,String> map = new Hashtable<>();
}
线程,向map中写入数据:
public void run() {
long start = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
Const.map.put(this.getName() + (i + 1), this.getName() + i + 1);
}
long end = System.currentTimeMillis();
System.out.println(this.getName() + " 结束!用时:" + (end - start) + " 毫秒");
}
测试类:
public class Demo {
public static void main(String[] args) throws InterruptedException {
Thread1A a1 = new Thread1A();
Thread1A a2 = new Thread1A();
a1.setName("线程1-");
a2.setName("线程2-");
a1.start();
a2.start();
//休息10秒,确保两个线程执行完毕
Thread.sleep(1000 * 5);
//打印集合大小
System.out.println("Map大小:" + Const.map.size());
}
}
执行结果:
可以看到,Hashtable保证的线程安全,时间是2秒多。
-
再看ConcurrentHashMap
公有、静态的集合:
public class Const { public static ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>(); }
线程,向map中写入数据:
public void run() { long start = System.currentTimeMillis(); for (int i = 0; i < 500000; i++) { Const.map.put(this.getName() + (i + 1), this.getName() + i + 1); } long end = System.currentTimeMillis(); System.out.println(this.getName() + " 结束!用时:" + (end - start) + " 毫秒"); }
测试类:
public class Demo { public static void main(String[] args) throws InterruptedException { Thread1A a1 = new Thread1A(); Thread1A a2 = new Thread1A(); a1.setName("线程1-"); a2.setName("线程2-"); a1.start(); a2.start(); //休息10秒,确保两个线程执行完毕 Thread.sleep(1000 * 5); //打印集合大小 System.out.println("Map大小:" + Const.map.size()); } }
执行结果:
- ConcurrentHashMap仍能保证结果正确,而且提高了效率。
HashTable效率低下原因:
public synchronized V put(K key, V value)
public synchronized V get(Object key)
HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。因为当一个线程访问HashTable的同步方法,其他线程也访问HashTable的同步方法时,会进入阻塞状态。如线程1使用put进行元素添加,线程2不但不能使用put方法添加元素,也不能使用get方法来获取元素,所以竞争越激烈效率越低。
ConcurrentHashMap高效的原因:CAS + 局部(synchronized)锁定