《面试题对标大纲》
题目列表集
1、你经常使用什么并发容器,为什么?
2、什么是Vector
3、ArrayList和Vector有什么不同之处?
4、为什么HashTable是线程安全的?
5、用过ConcurrentHashMap,讲一下他和HashTable的不同之处?
6、Collections.synchronized * 是什么?
7、 Java 中 ConcurrentHashMap 的并发度是什么?
8、什么是并发容器的实现?
9、Java 中的同步集合与并发集合有什么区别?
10、SynchronizedMap 和 ConcurrentHashMap 有什么区别?
11、CopyOnWriteArrayList 是什么?
12、CopyOnWriteArrayList 的使用场景?
13、CopyOnWriteArrayList 的缺点?
14、CopyOnWriteArrayList 的设计思想?
15、什么是并发队列
16、并发队列和并发集合的区别:
17、怎么判断并发队列是阻塞队列还是非阻塞队列
18、阻塞队列和非阻塞队列区别
19、常用并发列队的介绍?
20、常用的并发工具类有哪些?
1、你经常使用什么并发容器,为什么?
Vector、ConcurrentHashMap、HasTable
一般开发中容器用的最多的就是HashMap、ArrayList,LinkedList ,等等
但是在多线程开发中就不能乱用容器,如果使用了未加锁(非同步)的的集合,你的数据就会出问题。由此在多线程开发中需要使用的容器必须是加锁(同步)的容器。
2、什么是Vector
Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一
个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,访问它
比访问ArrayList慢很多
3、ArrayList和Vector有什么不同之处?
Vector方法带上了synchronized关键字,是线程同步的
Vector:
ArrayList:
4、为什么HashTable是线程安全的?
因为HasTable的内部方法都被synchronized修饰了,所以是线程安全的。其他的都和HashMap一 样
HashTable:
HashMap:
5、用过ConcurrentHashMap,讲一下他和HashTable的不同之处?
ConcurrentHashMap是Java5中支持高并发、高吞吐量的线程安全HashMap实现。它由Segment
数组结构和HashEntry数组结构组成。Segment数组在ConcurrentHashMap里扮演锁的角色,
HashEntry则用于存储键-值对数据。一个ConcurrentHashMap里包含一个Segment数组,
Segment的结构和HashMap类似,是一种数组和链表结构;一个Segment里包含一个HashEntry
数组,每个HashEntry是一个链表结构的元素;每个Segment守护着一个HashEntry数组里的元
素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。看不懂???很正常,我也看不懂
总结:
- HashTable就是实现了HashMap加上了synchronized,而ConcurrentHashMap底层采用分
段的数组+链表实现,线程安全 - ConcurrentHashMap通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效
率提升N倍,默认提升16倍。 - 并且读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。
- Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,
ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术 - 扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进
行扩容),插入前检测需不需要扩容,有效避免无效扩容
6、Collections.synchronized * 是什么?(注意:* 号代表后面是还有内容的)
此方法完完全全的可以把List、Map、Set接口底下的集合变成线程安全的集合
-
原理:应该是用到代理模式
7、 Java 中 ConcurrentHashMap 的并发度是什么?
ConcurrentHashMap 把实际 map 划分成若干部分来实现它的可扩展性和线程安全。这种划分是
使用并发度获得的,它是 ConcurrentHashMap 类构造函数的一个可选参数,默认值为 16,这样
在多线程情况下就能避免争用。在 JDK8 后,它摒弃了 Segment(锁段)的概念,而是启用了一种全新的方式实现,利用 CAS 算
法。同时加入了更多的辅助变量来提高并发度。
8、什么是并发容器的实现?(同步容器和并发容器的区别)
何为同步容器:可以简单地理解为通过 synchronized 来实现同步的容器,如果有多个线程调用同
步容器的方法,它们将会串行执行。比如 Vector,Hashtable,以及
Collections.synchronizedSet,synchronizedList 等方法返回的容器。可以通过查看 Vector,
Hashtable 等这些同步容器的实现代码,可以看到这些容器实现线程安全的方式就是将它们的状态
封装起来,并在需要同步的方法上加上关键字 synchronized。并发容器使用了与同步容器完全不同的加锁策略来提供更高的并发性和伸缩性,例如在
ConcurrentHashMap 中采用了一种粒度更细的加锁机制,可以称为分段锁,在这种锁机制下,允
许任意数量的读线程并发地访问 map,并且执行读操作的线程和写操作的线程也可以并发的访问
map,同时允许一定数量的写操作线程并发地修改 map,所以它可以在并发环境下实现更高的吞
吐量。
9、Java 中的同步集合与并发集合有什么区别?
- 同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更
高。在 Java1.5 之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的
扩展性。Java5 介绍了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和内部分
区等现代技术提高了可扩展性
10、SynchronizedMap 和 ConcurrentHashMap 有什么区别?
SynchronizedMap 一次锁住整张表来保证线程安全,所以每次只能有一个线程来访为 map。
ConcurrentHashMap 使用分段锁来保证在多线程下的性能。
ConcurrentHashMap 中则是一次锁住一个桶。ConcurrentHashMap 默认将hash 表分为 16 个
桶,诸如 get,put,remove 等常用操作只锁当前需要用到的桶。
这样,原来只能一个线程进入,现在却能同时有 16 个写线程执行,并发性能的提升是显而易见
的。另外 ConcurrentHashMap 使用了一种不同的迭代方式。在这种迭代方式中,当iterator 被创建后
集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时 new
新的数据从而不影响原有的数据,iterator 完成后再将头指针替换为新的数据 ,这样 iterator线程
可以使用原来老的数据,而写线程也可以并发的完成改变。
11、CopyOnWriteArrayList 是什么?
CopyOnWriteArrayList 是一个并发容器。有很多人称它是线程安全的,我认为这句话不严谨,缺
少一个前提条件,那就是非复合场景下操作它是线程安全的。CopyOnWriteArrayList(免锁容器)的好处之一是当多个迭代器同时遍历和修改这个列表时,不会抛
出 ConcurrentModificationException。在CopyOnWriteArrayList 中,写入将导致创建整个底层
数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全地执行。
12、CopyOnWriteArrayList 的使用场景?
合适读多写少的场景。
13、CopyOnWriteArrayList 的缺点?
由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致
young gc 或者 full gc。不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个 set 操作后,读取到数
据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求。由于实际使用中可能没法保证 CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点
多,每次 add/set 都要重新复制数组,这个代价实在太高昂了。在高性能的互联网应用中,这种
操作分分钟引起故障。
14、CopyOnWriteArrayList 的设计思想?
1、读写分离,读和写分开
2、最终一致性
3、使用另外开辟空间的思路,来解决并发冲突
15、什么是并发队列?
1、消息队列很多人知道:消息队列是分布式系统中重要的组件,是系统与系统直接的通信
2、并发队列是什么:并发队列多个线程以有次序共享数据的重要组件
16、并发队列和并发集合的区别?
那就有可能要说了,我们并发集合不是也可以实现多线程之间的数据共享吗,其实也是有区别的:
队列遵循“先进先出”的规则,可以想象成排队检票,队列一般用来解决大数据量采集处理和显示
的。并发集合就是在多个线程中共享数据的
17、怎么判断并发队列是阻塞队列还是非阻塞队列
在并发队列上JDK提供了 Queue接口,一个是以 Queue 接口下的
BlockingQueue 接口为代表的阻塞队列,另一个是高性能(无堵塞)队列。
18、阻塞队列和非阻塞队列区别
- 当队列阻塞队列为空的时,从队列中获取元素的操作将会被阻塞。
- 当阻塞队列是满时,往队列里添加元素的操作会被阻塞。
- 试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。
- 试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲
起来
19、★ 常用并发列队的介绍?
一、 非堵塞队列:
1、ArrayDeque, (数组双端队列)
ArrayDeque (非堵塞队列)是JDK容器中的一个双端队列实现,内部使用数组进行元素存
储,不允许存储null值,可以高效的进行元素查找和尾部插入取出,是用作队列、双端队
列、栈的绝佳选择,性能比LinkedList还要好。
2、PriorityQueue, (优先级队列)
PriorityQueue (非堵塞队列) 一个基于优先级的无界优先级队列。优先级队列的元素按照其自
然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用的构造
方法。该队列不允许使用 null 元素也不允许插入不可比较的对象
3、ConcurrentLinkedQueue, (基于链表的并发队列)
ConcurrentLinkedQueue (非堵塞队列): 是一个适用于高并发场景下的队列,通过无锁的方
式,实现了高并发状态下的高性能。ConcurrentLinkedQueue的性能要好于BlockingQueue接
口,它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。该队列不允
许null元素。
二、堵塞队列:
1. DelayQueue, (基于时间优先级的队列,延期阻塞队列)
DelayQueue是一个没有边界BlockingQueue实现,加入其中的元素必需实现Delayed接口。
当生产者线程调用put之类的方法加入元素时,会触发Delayed接口中的compareTo方法进行
排序,也就是说队列中元素的顺序是按到期时间排序的,而非它们进入队列的顺序。排在队
列头部的元素是最早到期的,越往后到期时间赿晚。
2、ArrayBlockingQueue, (基于数组的并发阻塞队列)
ArrayBlockingQueue是一个有边界的阻塞队列,它的内部实现是一个数组。有边界的意思是它
的容量是有限的,我们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变。
ArrayBlockingQueue是以先进先出的方式存储数据
3、LinkedBlockingQueue, (基于链表的FIFO阻塞队列)
LinkedBlockingQueue阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,它就是
有边界的,如果不指定,它就是无边界的。说是无边界,其实是采用了默认大小为
Integer.MAX_VALUE的容量 。它的内部实现是一个链表。
4、LinkedBlockingDeque, (基于链表的FIFO双端阻塞队列)
LinkedBlockingDeque是一个由链表结构组成的双向阻塞队列,即可以从队列的两端插入和移除
元素。双向队列因为多了一个操作队列的入口,在多线程同时入队时,也就减少了一半的竞争。
相比于其他阻塞队列,LinkedBlockingDeque多了addFirst、addLast、peekFirst、peekLast等方法,
以first结尾的方法,表示插入、获取获移除双端队列的第一个元素。以last结尾的方法,表示插入、获
取获移除双端队列的最后一个元素。
LinkedBlockingDeque是可选容量的,在初始化时可以设置容量防止其过度膨胀,如果不设置,默认
容量大小为Integer.MAX_VALUE
5、PriorityBlockingQueue, (带优先级的无界阻塞队列)
priorityBlockingQueue是一个无界队列,它没有限制,在内存允许的情况下可以无限添加元素;
它又是具有优先级的队列,是通过构造函数传入的对象来判断,传入的对象必须实现comparable
接口。
6、SynchronousQueue (并发同步阻塞队列)
SynchronousQueue是一个内部只能包含一个元素的队列。插入元素到队列的线程被阻塞,直到
另一个线程从队列中获取了队列中存储的元素。同样,如果线程尝试获取元素并且当前不存在任何
元素,则该线程将被阻塞,直到线程将元素插入队列。
将这个类称为队列有点夸大其词。这更像是一个点。
三、并发队列常用的方法
不管是那种列队,是那个类,当是他们使用的方法都是差不多的
20、常用的并发工具类有哪些?
1、CountDownLatch
CountDownLatch 类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有
一个任务A,它要等待其他3个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来
实现这种功能了。
2、CyclicBarrier (回环栅栏)
CyclicBarrier它的作用就是会让所有线程都等待完成后才会继续下一步行动。
CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数。
当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。CyclicBarrier初始时还可带一个Runnable的参数, 此Runnable任务在CyclicBarrier的数目达到
后,所有其它线程被唤醒前被执行。
3、Semaphore (信号量)
Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量(允许自定义多少线程同时访问)。
就这一点而言,单纯的synchronized 关键字是实现不了的。