其他资料
目录
2.请说一下ArrayList和LinkedList的区别?
4.ConcurrentHashMap和hashmap的区别是什么?
4.java中Synchronized和ReentrantLock有什么区别?
1.java中有哪些集合类?
Java 中的集合类主要分为两大类:Collection 接口和 Map接囗
前者是存储对象的集合类,后者存储的是键值对(key-value)
Collection 接口下又分为 List、Set 和 Queue 接口,每个接口有其具体实现类,Map 接口下有很多接口,比如HashMap,inkedHashMap,TreeMap,Hashtable,ConcurrentHashMap
List下有ArrayList(基于动态数组,查询速度快,插入、删除慢),LinkedList(基于双向链表,插入、删除快,查询速度慢)Vector(线程安全的动态数组,类似于 ArrayList,但开销较大);
Set下有HashSet(基于哈希表,元素无序,不允许重复),LinkedHashSet(基于链表和哈希表,维护插入顺序,不允许重复)TreeSet(基于红黑树,元素有序,不允许重复);
Queue下有PriorityQueue(基于优先队列,元素有序),LinkedList(可以作为常规队列使用,先进先出)
关于hashmap的知识点请参考以下文章:每日速记10道java面试题05-CSDN博客
2.请说一下ArrayList和LinkedList的区别?
两者的底层数据结构不一样,ArrayList是用数组实现的,LinkedList是用链表实现的。
插入和删除的效率不同,ArrayList删除和插入元素效率较低,LinkedList删除和插入元素效率比较高,只需要移动指针即可。
随机访问的效率不同,ArrayList底层是由数组实现的,因此随机访问效率高,时间复杂度为O(1),LinkedList需要从头开始遍历链表,时间复杂度为O(n)
3.讲讲ArrayList的扩容机制
1.ArrayList
构造函数允许指定初始容量。如果不指定,ArrayList
默认的初始容量为 10。
2.当 ArrayList
中的元素数量达到当前数组的容量时,需要添加更多元素,ArrayList
会进行扩容操作。扩容操作通常涉及创建一个新的数组,其容量是原数组容量的 1.5 倍(newCapacity = oldCapacity + (oldCapacity >> 1)
),即原容量加上原容量的一半。
3.创建新数组后,ArrayList
会将原数组中的所有元素复制到新数组中。
4.复制完成后,ArrayList
会将内部的数组引用指向新的数组。
延伸→那旧数组最终去哪了呢?→被JVM内部的垃圾回收机制给回收掉了。
4.ConcurrentHashMap和hashmap的区别是什么?
HashMap
是非线程安全的,这意味着在多线程环境中,如果多个线程同时对HashMap
进行读写操作,可能会导致数据不一致或抛出ConcurrentModificationException
。ConcurrentHashMap
是线程安全的,它允许多个线程同时对容器进行读写操作而不会导致数据不一致。
延伸→那ConcurrentHashMap
是如何保证线程安全的呢?
分段锁机制(Java 7):
在 Java 7 中,ConcurrentHashMap
使用分段锁技术,将哈希表分成多个段(Segment),每个段相当于一个小的 HashMap
,并且继承自 ReentrantLock
,即每个段拥有一个独立的锁。这样,当多个线程访问 ConcurrentHashMap
时,只要它们访问的不是同一个段,就可以实现真正的并发访问,从而减少锁竞争,提高并发性能。
CAS + synchronized 机制(Java 8):
在 Java 8 中,ConcurrentHashMap
对实现方式进行了改进,放弃了分段锁,而是采用了“CAS + synchronized”的机制来保证线程安全。如果某个桶没有元素,那么使用 CAS 操作来添加新节点;如果有元素,则使用 synchronized
锁住当前桶,再次尝试 put 操作。这样可以避免分段锁机制下的锁粒度太大,提高了并发性能。
以下为java并发的知识点
1.什么是java中的线程同步?
当一个共享资源被多个线程访问时进行保护的行为叫线程同步,让线程有序访问,目的是为了避免出现数据不一致等问题。
延伸→如何保证线程同步?→常见的线程同步的方式1.Synchronized 2.ReentrantLock
这里可能会给自己挖坑,把面试官待到Synchronized 和ReentrantLock两个知识点来
延伸→java中的Synchronized是怎么实现的?→问题2
延伸→java中的ReentrantLock是怎么实现的?→问题3
延伸→java中Synchronized和ReentrantLock有什么区别?→问题4
2.java中的Synchronized是怎么实现的?
synchronized 实现原理依赖于JVM 的 Monitor(监视器锁)和对象头(Object Header)。
synchronized 修饰在方法或代码块上时,会对特定的对象或类加锁,从而确保同一时刻只有一个线程能执行加锁的代码块。当synchronized 修饰方法:方法的常量池会增加一个ACCSYNCHRONIZED 标志,当某个线程访问这个方法检查是否有 ACC SYNCHRONIZED 标志,若有则需要获得监视器锁才可执行方法,此时就保证了方法的同步。
synchronized 修饰代码块:会在代码块的前后插入 monitorenter和 monitorexit 字节码指令。可以把 monitorenter 理解为加锁,monitorexit 理解为解锁。
3.java中的ReentrantLock是怎么实现的?
1.ReentrantLock是基于AQS实现的支持可重入,公平/非公平,可中断的锁。
2.内部通过一个state变量和两个队列(同步队列和等待队列)来实现
3.同步队列存放的是需要竞争这个锁的线程,是双向链表,等待队列存放的是需要满足condition才执行的线程,是单向链表。
4.锁是否公平的区别在于:竞争锁时,是否需要判断自身是否是队列中的第一个线程,公平铁需要判断,非公平锁则不用,直接尝试获取即可,非公平锁和公平锁获取失败都需要加入同步队列。
5.公平锁在获取锁时会判断当前线程是否已经是同步队列的第一个线程,或同步队列为空
延伸→什么是AQS?问题5
4.java中Synchronized和ReentrantLock有什么区别?
锁的获取方式:synchronized 是隐式获取锁的,即在进入 synchronized 代码块或方法时自动获取锁,退出时自动释放锁;而ReetrantLock 需要程序显式地获取锁和释放锁。
锁的性质:synchronized 是可重入的互斥锁,即同一个线程可以多次获得同一把锁,而且锁的释放也只能由获得锁的线程来释放,ReetrantLock 可以是可重入的互斥锁,也可以是非可重入的互斥锁,还可以是读写锁。
锁的粒度:synchronized 是以代码块和方法为单位进行加锁和解锁,而 ReetrantLock 可以精确地控制铁的范围,可以支持多个条件变量。
性能:在低并发的情况下,sychronized 的性能优于ReetrantLock,因为 ReetrantLock 需要显式地获取和释放锁,而synchronized 是在M 层面实现的;在高并发的情况下,ReetrantLock 的性能可能优于 synchronized,因为 Lock 可以更好地支持高并发和读写分离的场景。
5.什么是AQS?
AQS 是 AbstractQueuedSynchronizer 的缩写,它是 Java 并发包 java.util.concurrent.locks
中的一个抽象类,提供了一个用于构建锁和其他同步器的框架。
简单来说 AQS 就是起到了一个抽象、封装的作用,将一些排队、入队、加锁、中断等方法提供出来,便于其他相关 JUC 锁的使用,具体加锁时机、入队时机等都需要实现类自己控制。
常见的实现类有ReentrantLock、CountDownLatch、Semaphore等等