杂谈(三)
HashMap和TreeMap的区别是什么?
- HashMap的元素是没有顺序的,而TreeMap所有的元素都是有某一固定顺序的,如果需要得到有序的结果,就用TreeMap
- 二者都不是线程安全的
- HashMap继承AbstractMap类,TreeMap继承SortedMap类
- TreeMap基于红黑树实现
- HashMap适用于Map插入、删除、定位元素,TreeMap适用于按自然顺序或自定义顺序遍历键
- 在HashMap中判断key是否重复的依据是根据hash值和equals比较,但是在TreeMap中,判断key是否重复的依据是根据
comparaTo是否为0,如果为0,TreeMap 就认为key是重复的。
TreeMap排序是怎么实现的,它的数据结构是什么样的?
TreeMap去重实现:
- 定义一个指针先指向二叉树的向根节点
- 将传入的key与指针节点的key对比,如果传入的key大于指针节点的key,就将指针移动到当前节点的右子树,如果传入的key小于指针节点的key,就将指针移动到当前节点的左子树。
- 循环执行上一步,直到传入的key值与指针节点的key值相等,就将value值替换掉,或者指针已经移动到底了,就将传入的key和value作为新的节点,添加到树的底部
- 为了维护红黑树的平衡,将一些节点翻转
TreeMap排序:
由于TreeMap在存入节点的时候已经是按照排序二叉树的特性来插入了,所以在遍历二叉树的时候只要用中序遍历,就可以按照从小到大的顺序读取每一个节点了。
ConcurrentHashMap怎么保证线程安全的?
在jdk1.7中是采用Segment + HashEntry + ReentrantLock的方式进行实现的,而1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现。
-
JDK1.8的实现降低锁的粒度,JDK1.7版本锁的粒度是基于Segment的,包含多个HashEntry,而JDK1.8锁的粒度就是HashEntry(首节点)
-
JDK1.8版本的数据结构变得更加简单,使得操作也更加清晰流畅,因为已经使用synchronized来进行同步,所以不需要分段锁的概念,也就不需要Segment这种数据结构了,由于粒度的降低,实现的复杂度也增加了
-
JDK1.8使用红黑树来优化链表,基于长度很长的链表的遍历是一个很漫长的过程,而红黑树的遍历效率是很快的,代替一定阈值的链表,这样形成一个最佳拍档
-
在1.8中ConcurrentHashMap的get操作全程不需要加锁,这也是它比其他并发集合比如hashtable、用Collections.synchronizedMap()包装的hashmap;安全效率高的原因之一。
-
get操作全程不需要加锁是因为Node的成员val是用volatile修饰的和数组用volatile修饰没有关系。
-
数组用volatile修饰主要是保证在数组扩容的时候保证可见性。
守护线程与普通线程
- 定义:守护线程又称为“服务线程”。在没有用户线程可服务时会自动离开。
- 优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
- 设置:通过setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为守护线程的方式是在 线程对象创建 之前 用线程对象的setDaemon方法。
- 举例说明:垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。
- 生命周期:守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则JVM不会退出。
在线程池中创建线程的流程是什么?
线程池的主要流程可以分为以下几步:
-
1.获取线程池状态,线程池状态正确,执行2 线程池状态不正确返回false。
-
2.判断线程池中数目和传入的要求是否一致。不一致返回false,一致执行3
-
3.CAS增加线程池的线程数(有个记录线程数的变量)。成功执行4,不成功执行1
-
4.获取线程池的锁,创建一个worker(也是线程)。
-
5.判断线程池的状态,状态正确。执行6,不正确返回false
-
6.将新添加的work放入存放线程的hashset中。启动线程成功!返回true,启动失败执行7
-
7.将刚才添加的线程从hashset中移除,然后返回false。
线程池的submit()和execut()有什么区别?
他们的区别:
1、execut()可以添加一个Runable任务,submit()不仅可以添加Runable任务还可以添加Callable任务。
2、execut()没有返回值,而submit()在添加Callable任务时会有返回值(再添加Runable任务时也有,不过无意义),可以通过返回值来查看线程执行的情况。
3、如果发生异常submit()可以通过捕获Future.get抛出的异常,而execute()会终止这个线程。
http短连接和长连接的区别
在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。
而从HTTP/1.1起,默认使用长连接,用以保持连接特性。