java1.0中的容器只有Vector和HashTable这两个
- Vector是一个一个往里扔的,实现了List接口
- HashTable是一对一对往里扔的,实现了Map接口
- 这两个方法在设计之初的问题—->这两个容器中的所有方法都是默认加synchronized的,效率有点差
- 也不能说他们性能极差,因为synchronized现在的性能也并不是十分的差(重量级锁—->锁升级)
- 原来设计的时候,他认为所有的方法必须是线程安全的,说他们效率差正是因为多数的时候只有一个线程在运行,是没有必要加synchronized的
之后意识到没必要一定加synchronized就设计了HashMap
- HashMap完全没有加锁
- 与HashTable除了synchronized有无的区别之后,还有一些其他区别
- Sun在最初的HashTable上又添加了一些东西,比HashTable好用一些
- 但是HashMap中没有锁的东西,为了让HashMap既可以用于那些要锁的环境,又可以用于不需要锁的环境,又添加了一个Collections类—->容器的一些
- Collections类中有一个synchronizedMap方法,这个方法会把不加锁的HashMap变成加锁的版本
- HashMap有两个版本:一个是不带锁的版本,一个是通过synchronizedMap实现的带锁的版本
- HashTable是最早的容器,是遗留下来的容器(jdk1.0),很多设计上有一些缺陷,所以现在HashTable和Vector基本不用
- 面试的时候也许会从数据结构方面问一些HashTable的内容—->了解一下
- Vector和HashTable自带锁,但是一般不用
- HashMap本身不带锁,使用synchronizedMap加的锁又是很重的锁,加锁的时候是直接返回SynchronizedMap这种类型,用的锁还是synchronized—->和HashTable的效率区别不大,里面定义了一个要加锁的对象(要锁住的对象),然后使用synchronized代码块的方式进行加锁
- HashTable是将整个方法上全部加锁,但是SynchronizedMap的锁的粒度上就相对来说小了一些,可能能提高一点效率—->效率仍不是特别高
在JUC包出来后,又添加了一个新的效率更高的ConcurrenctHashMap
HashTable在某些情况下(在某些方面)的效率未必比其他的差(synchronized某些方面的效率未必比其他的差)
- 下面的程序中测试了每一个容器使用时的效率
- 哪个效率高,哪个效率低,不要想当然,一定要写代码来进行测试—->可以用JMH测试框架
- 多少个键值对(UUID对装到容器中)
- 多少个线程
- 这两个值变化会影响效率上的变化
- 性能测试有时候需要程序员来写—->一般测试开发来写,但是一般开发的程序员也需要了解性能测试这一块的—->小公司只能自己测试
- 做测试的时候,把要用到的数据先生成好,而不是到真正要用的时候才去生成这个数据,因为测试的时候用到的测试用例应该是完全一样的(是同样的100万对),要是每次重新生成,会生成不一样的内容,这样测试的时候就会有一些干扰因素
- 测试用例得先准备好,测不同的东西的时候要用同样的测试用例才能会有更精确的区别;用不同的测试用例,看不出是因为测试用例不一样的原因,还是因为容器不一样的原因,会区分不开—->**控制变量**
Constants常量类
package com.mashibing.juc.c_023_02_FromHashtableToCHM;
public class Constants {
public static final int COUNT = 1000000;
public static final int THREAD_COUNT = 100;
}
HashTable
package com.mashibing.juc.c_023_02_FromHashtableToCHM;
import java.util.Hashtable;
import java.util.UUID;
public class T01_TestHashtable {
static Hashtable<UUID, UUID> m = new Hashtable<>();
static int count = Constants.COUNT;
static UUID[] keys = new UUID[count];
static UUID[] values = new UUID[count];
static final int THREAD_COUNT = Constants.THREAD_COUNT;
static {
for (int i = 0; i < count; i++) {
keys[i] = UUID.randomUUID();
values[i] = UUID.randomUUID();
}
}
static class MyThread extends Thread {
int start;
int gap = count/THREAD_COUNT;
public MyThread(int start) {
this.start = start;
}
@Override
public void run() {
for(int i=start; i<start+gap; i++) {
m.put(keys[i], values[i]);
}
}
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
Thread[] threads = new Thread[THREAD_COUNT];
for(int i=0; i<threads.length; i++) {
threads[i] =
new MyThread(i * (count/THREAD_COUNT));
}
for(Thread t : threads) {
t.start();
}
for(Thread t : threads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println(end - start);
System.out.println(m.size());
//-----------------------------------
start = System.currentTimeMillis();
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(()->{
for (int j = 0; j < 10000000; j++) {
m.get(keys[10]);
}
});
}
for(Thread t : threads) {
t.start();
}
for(Thread t : threads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
end = System.currentTimeMillis();
System.out.println(end - start);
}
}
HashMap不是同步的—->有可能会报异常
- 因为里面会将HashMap中的Node变成TreeNode?
- 内部没有锁,所以多线程访问的时候会出问题,往里插没有实际意义
使用SynchronizedMap给HashMap手动加锁
- 底层是自己定义了一个mutex的对象,然后通过synchronized对mutex加锁
- 严格意义上讲,这种方式和HashTable的效率差别不会太大—->加锁的粒度不同,本质上都是通过synchronized
实际中在多线程中真正用得最多的:ConcurrentHashMap
- 以后实际中多线程一般用的都是ConcurrentHashMap
- ConcurrentHashMap的效率比HashTable和SynchronizedMap高?
- ConcurrentHashMap的效率主要提高在读上面
- 往里面插的时候,内部做了多种判断,本来是链表,到8之后还要变成红黑树,里面还做了各种CAS的判断,所以往里面插的效率会更低一点
- HashTable和SynchronizedMap虽然读的时候效率低一些,但是往里面插的时候只需要加锁就行了,没有那么复杂的判断
- 要用哪一个数据结构,还是要看实际中的需求
ConcurrentHashMap的效率提升在读上面,而不是在写上面,写的时候反而是HashTable和SynchronizedMap效率更高—->在实际中选择用哪一种的时候往往要进行压测,以实测为准
- HashTable读的时间是32s
- SynchronizedMap读的时间是38s
- ConcurrentHashMap读的时间是1.7s
- HashTable写的时间在1s以内
- SynchronizedMap写的时间在1s以内
- ConcurrentHashMap写的时间超过了1s
- 这就是为什么用ConcurrentHashMap的原因
- ConcurrentHashMap并发插入的时候效率未必会比HashTable和SynchronizedMap效率高
- ConcurrentHashMap读取的时候效率特别高
多数情况下,写的情况比较少,多数读的情况比较多
从Vector发展到ConcurrentList基本上也是这样一个发展过程(历程)
- 发展过来,他们的区别和HashTable发展到ConcurrentHashMap的区别差不多