目录
ConcurrentHashMap和HaspTable的区别
一、什么是JUC
JUC,全称java.util.concurrent,是java.util下一个关于多线程的库,里面包含了许多工具类用于多线程的开发
二、常见组件
Callable
Callable是一个类似于Runnable的接口,它弥补了Runnable返回类型只能为void的缺陷,在Callable中有一个call()方法,它对标为Runnable的run()方法,但是call()可以拥有返回值
public static void main(String[] args) throws InterruptedException {
Callable<Integer> callable = new Callable<Integer>() {//泛型参数写什么,返回值就是什么
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 1000; i++) {
sum+=i;
}
return sum;
}
};
FutureTask<Integer> futureTask = new FutureTask<>(callable);//callable不能直接作为参数传给Thread,需要包装一层FutureTask
Thread t = new Thread(futureTask);
t.start();
System.out.println(futureTask.get());
}
注意:callable类型不能直接作为参数传给Thread,需要包装一层FutureTask,然后将futuretask作为参数传给Thread。
就相当于我们去餐馆点餐,点完餐后,服务员给我们一张小票,随后通过小票来取菜。futuretask就是这张小票。通过小票才能调用call()方法。
最后在使用futuretask.get()方法来获取结果即可。
ReentrantLock
这是一把可重入锁,它单独提供了lock和unlock两个独立方法来进行加锁解锁
它的好处体现在三个方面
- synchronized只是加锁解锁,如果加锁时发现锁被占用,只能阻塞等待,而ReentranLock还提供了一个tryLock方法,即加锁失败不会阻塞等待,直接返回一个false,这样就可以灵活控制锁,让程序员决定接下来咋做
- synchronized是一个非公平锁,而ReentranLock提供了公平和非公平两种工作模式,在构造方法中,传入true开启公平锁。
- synchronized搭配wait notify进行等待唤醒,如果多个线程同时waIt,notify随机唤醒,而ReentranLock搭配Condition这个类起到等待通知,功能更加强大
信号量Semaphore
信号量,本质上是一个计数器,描述了当前可用资源的个数,它的主要操作在于PV操作
P操作:申请资源。计数器-1
V操作,释放资源,计数器+1
CountDownLatch
CountDownLatch是 Java 并发包中用于同步线程的工具类,它可以使一个或多个线程等待一组事件发生后再执行。
例如在一场跑步比赛中,发号枪令开始,这场比赛结束的时间取决于最后一个选手冲过终点的时间,而整个类就可以感知到最后一个选手是什么时候冲过终点的
应用场景:多线程下载
下载一个比较大的文件,如果是单线程下载,速度就远远不如多线程下载,因为我们的带宽正常情况下是无法跑满的,受限于服务器设置,但是如果把这一个文件分多个线程去跑,每一个线程都跑满实际速度,这就可以跑满带宽,下载速度也就快了很多
二、线程安全集合类
常用的一些集合类,例如ArrayList,LinkedList,HashMap.....这些在多线程下都是线程不安全的。为了解决这些问题,有如下几种解决方案
- 手动加锁
- 使用Collections.synchronizedList(new ArrayList):这是标准库提供的一层包装,在参数列表内添加集合类,系统会自动给这些集合类加锁实现线程安全
Collections.synchronizedList(new ArrayList);
- CopyOnWriteArrayList:支持写时拷贝的集合类,两个线程同时修改同一个变量,这个类就会在修改的时候拷贝一份一模一样的数据进行修改,这样就变成了两个线程同时修改两个变量,也实现了线程安全,它虽然不加锁,但是拷贝开销很大,适合于修改不频繁元素个数少的场景
多线程使用哈希表
- HashMap是线程不安全的,不能使用
- HashTable是线程安全的,原理在于给关键方法加锁,但不推荐使用
- ConcurrentHashMap是线程安全的,推荐使用
ConcurrentHashMap和HaspTable的区别
1.加锁粒度不同:HashTable针对整个哈希表加锁,任何增删改查的操作都会触发锁竞争
而ConcurrentHashMap在加锁时以哈希桶每一个头结点为锁对象进行加锁,这样每次插入元素时如果插入的是不同结点的数据就不会触发锁竞争,大部分的插入操作就不会触发锁竞争了
2.更充分的利用了CAS机制
3.优化了扩容策略:HashMap在扩容时,会申请内存空间,然后把元素从旧的哈希表上删除,插入到新的哈希表上,如果元素非常多,每扩容一次,成本就会非常高,导致非常卡顿。而ConcurrentHashMap在扩容时不会直接搬运所有元素,它会在申请空间后,在每次进行插入操作时都往新表插入,然后顺便搬运一小部分元素到新表,直到数据全部搬完之后,删除旧表。