集合数据结构&线程并发库
1.ArrayList与LinkedList区别?
ArrayList:底层基于数组实现,有序可重复集合,是线程不安全的,查改快。因为可以使用数组索引直接定位到数据,所以查询和修改效率快;而数组的长度一旦确定不能更改,每次添加数据的时候都需要对数组容量进行判断是否需要扩容,如果一旦需要扩容会创建一个原来数组长度1.5倍的新数组并将原数组中数据拷贝到新数组中,所以添加效率比较低 。
LinkedList:底层基于双向链表,有序可重复集合,是线程不安全的,增删插快。因为添加数据的时候只需要新建一个节点放到链表的头部或者末尾即可,不会涉及到数据的复制,所以添加删除速度快;而在查询数据的时候需要去遍历整个链表效率较低,所以查询效率低。
2.HashSet与TreeSet如何去重?
TreeSet:无序不可重复集合,是程序不安全的。可以存放任意类型任意多个数据,如果容器内已经存储了一种类型那么类型就必须一致。通过类实现Comparable接口,重写CompareTo方法去重,jkd中实现该接口的默认都是升序,是自然排序;创建TreeSet对象的时候,将一个比较器Comparetor实现类对象传入,重写Compare方法去重,是定制排序
HashSet:底层基于hashMap,基于hashCode(),无序不可重复集合,是程序不安全的。先判断当前元素和要添加元素hashCode()是否相等,若相等再判断当前元素和要添加的元素equals()是否相等,若相等,则认为是两个相同的元素,则进行去重
3.List与Set的遍历方式有哪些?
List:普通for循环 、foreach、单向迭代器、双向迭代器
Set:单向迭代器、 foreach
4.HashMap HashTable ConcurrentHashMap Properties区别?
HashMap:基于hashCode(),是一个线程不安全, key可以有一个null, value可以有多个null
HashTable:是一个线程安全HashMap,key和value不能有null ,底层基于Entry(键值对)的数组、链表、红黑树,
使用sychonized同步put方法
ConcurrentHashMap :效率高,线程安全,key和value不能有null
Properties:是HashTable的子类,常用来做配置文件、框架使用,key和value都是String类型,通过List 方法 、Load方法 读取和写入
5.HashMap存值流程是怎样的? 取值过程是怎样的?
存值:1.根据Key进行Hash运算得到一个int值
2.判断是否已经有数组 ,如果没有需要进行初始化,调用resize方法创建数组
3.根据int值和数组的最大索引值计算出当前entry在数组中存放的位置,用key的hashCode()%map.length
4.判断该位置是否已经存在entry如果不存在则直接创建一个新的node放在该位置
5.如果存在判断key是否完全相同 ,如果相同则返回被覆盖的值
6.如果key不相同 ,形成链表追加到链表末尾,当链表长度等于8时,优化为红黑树,当调用方法remove(K key),减少到6时,退回到链表结构
7.判断数组的使用长度是否已经超过加载因子所约定的长度,调用resize方法进行数组扩容,扩容为原来的两倍,扩容后length值发生改变,要重新计算hashCode值
取值:1.根据key计算int值
2.判断数组是为空如果为空直接返回null
3.如果存在判断该位置的第一个元素位置是否为要查询的key,如果是直接返回value
4.如果不是则判断它的下一个是红黑树还是链表
5.如果是红黑树则按照红黑树的查找方式返回值
6.如果是链表则按照链表的方式遍历匹配返回值
6.线程与进程区别?
线程:最小的执行单位
进程:计算机分配和调度资源的最小单位
7.线程的创建方式有哪些?
继承Thread类、实现Runnable接口、实现Callable和Future创建线程
8.线程的状态有哪些?常用的线程方法有哪些?
状态:
创建 ----> 就绪 ----> 执行 ----> 死亡(在执行时可能会产生阻塞,恢复时又回到就绪状态)
(1) 创建:线程刚被创建
(2) 就绪:线程已经被启动,正在等待被分配给CPU时间片
(3) 执行:获得CPU分配的时间片,正在执行任务
(4) 阻塞:由于某种原因导致正在运行的线程让出CPU并暂停自己的执行(如CPU分配的时间片用完)
(5) 死亡:线程中的所有代码、任务完成或被其他线程杀死
方法:
sleep():使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行
join():等调用该方法的线程执行完毕后再往下继续执行
yield():与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会
deamon():当所有非后台线程结束时,程序也就终止,同时会杀死所有后台线程
9.线程安全问题是什么?如何解决?
多个线程同时去访问同一个数据的时候有可能造成线程安全问题。
1.同步:synchronize
2.加锁:ReentrantLock
3.线程本地存储:ThreadLocal
10.乐观锁怎么实现?
加上version字段,每一次的操作都会更新version,提交时如果version不匹配,停止本次提交,可以尝试下一次的提交,以保证拿到的是操作1提交后的结果
11.ThreadLocal如何保证线程安全?
ThreadLocal是线程的一个本地存储(本地变量),为每个使用该变量的线程分配一个独立的变量副本,且在线程内部任何地方都可以使用,线程之间互不影响,获取以当前的线程去找到对应的map然后获取到对应的值
12.notify与notifyall区别?
notify :如果有多个线程等待,随机唤醒一个获得对象资源
notifyall :唤醒所有的等待线程竞争锁资源
13.sleep 与wait区别?
sleep:来自Thread类,不释放同步锁,必须捕获异常
wait:来自Object类,释放同步锁,不需要捕获异常
由于sleep()方法是Thread类的方法,因此它不能改变对象的机锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的机锁没有被释放,其他线程仍然无法访问这个对象。而wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对
14.run方法与start方法区别?
run:不会开启新的线程,相当于对象调用普通方法
start:会开启一个新线程,它会自动调用线程中的run方法
15.synchronized与lock区别?
加锁相对于同步而言灵活性更高效率较高,同步由jvm来维护,加锁是代码级别的需要手动释放否则会造成死锁