面试邀请来得有突然,记录一下有些问题以及答案。
ArrayList 是线程安全的吗,怎样保证他线程安全。
原因:
其实大部分集合类都不是线程安全的,其关键的原因在于添加元素的底层实现,因为在arrayList的add()中:
elementData[size++] = e;
这一步包括了自增和赋值,因为当线程A执行了赋值以后,暂停转而运行其他的线程,但是还没扩容,就会导致,B线程的值直接覆盖A的值,
解决:
调用Collections.synchronizedList方法 ,其原理也就是将ArrayList里的方法添加synchronized关键字,也就是加锁,也就是经典的装饰器模式。
HashMap会出现什么问题,如何解决?
首先HashMap是一个高效的数据结构,可以在O(1)时间复杂度查询数据,原理大致为:将Key通过Hash算法得到一个地址值,再获取内存中想要的数据,但是Hash算法存在缺陷,就是不同的Key值通过计算得到的地址值又很小的几率相同,这就是Hash冲突。
解决方法:
-
开放寻址法:
它的核心思想就是重新探测新的位置来解决冲突,如何寻找新的地址:
1.线性探测法:
当向Hash表中插入数据时,发现该地址已经有数据了,那就从这个位置开始在数组中往后查找,直到找到空闲位置为止;缺点是随着不断填充,空闲的位置变少,探测的时间就会增加,
2.二次探测法:
探测的下标为hash(key)+0² 、 hash(key)+1² …
3.双重哈希法:
使用多个hash函数计算key ,如果第一个hash函数计算出来的地址被占用了,就调用第二个函数 ,直到找到空闲位置为止; -
链表法:
该方法更常用,原理为:在hash表中每一个桶或者槽(slot) 都会对应一个链表,将hash值相同的元素放到相同槽位的对应链表中,插入数据的时候,只需要通过hash函数计算出对应的hash值,然后添加到链表之后。链表法性能取决于hash函数的性能,尽量保证链表长度足够小。
如何创建一个线程池,其中的参数如何设置?
-
通过Executors类提供的静态方法Executors.newCachedThreadPool()
创建一个可缓存的线程池,若线程数超过所需,缓存一段时间后会回收,若线程数不够,则新建线程,初始状态下线程池没有线程,而线程不足会自动创建线程。 -
通过Executors类提供的静态方法Executors.newFixedThreadPool(3);
创建一个固定大小的线程池 -
通过Executors类提供的静态方法Executors.newScheduledThreadPool(3);
创建一个周期性的线程池,支持定时及周期性执行任务。
private static void createScheduledThreadPool() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
System.out.println(DateUtil.now() + " 提交任务");
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.schedule(() -> {
// 获取线程名称,默认格式:pool-1-thread-1
System.out.println(DateUtil.now() + " " + Thread.currentThread().getName() + " " + index);
// 等待2秒
sleep(2000);
}, 3, TimeUnit.SECONDS);
}
}
创建大小为3的线程池,设置定时时间为3秒
-
通过Executors类提供的静态方法Executors.newSingleThreadExecutor();
创建只有一个线程的线程池 -
通过自定义ThreadPoolExecutor构造器(重要)
ThreadPoolExecutor类提供了4种构造方法,可根据需要来自定义一个线程池。
共有7个相关参数
1. corePoolSize 核心线程数,线程池始终存活线程的数量
2. maximumPoolSize 最大线程数,线程池中允许的最大线程数。keepAliveTime
3. keepAliveTime 线程没有任务执行时最多保持多久时间会终止
4. unit 时间单位
5. workQueue 堵塞队列 有7中选择
- ArrayBlockingQueue 一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue 一个由链表结构组成的有界阻塞队列
- SynchronousQueue 一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
- PriorityBlockingQueue 一个支持优先级排序的*阻塞队列。
- DelayQueue 一个使用优先级队列实现的*阻塞队列,只有在延迟期满时才能从中提取元素。
- LinkedTransferQueue 一个由链表结构组成的*阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
- LinkedBlockingDeque 一个由链表结构组成的双向阻塞队列
6. threadFactory 线程工厂 主要用来创建线程
7. handler 拒绝策略 拒绝处理任务时的策略 4种
- AbortPolicy 直接拒绝 抛出异常
- CallerRunsPolicy 重试 再次调用运行该任务的execute()
- DiscardOldestPolicy 抛弃头部(最旧) 的任务,并执行当前任务
- DiscardPolicy 抛弃当前任务
JVM虚拟就内存结构:
我把它分为两类:线程公有 线程私有
java堆和方法区是线程共有的
java栈、本地方法栈、PC寄存器是每个线程独有一份
- 方法区主要是保存类的信息以及各种静态变量和常量;
- java堆主要保存实例对象
- java栈是程序方法运行的结构,每个方法都单独存入栈中,各自存有局部变量表操作数栈、以及动态链接和方法出入口
- 本地方法栈是为以native关键字修饰的方法服务
- PC寄存器是记录下一条需要执行的字节码指令
待续