11、HashMap 和 hashTable 区别?
区别:Hashtable是线程安全的,效率比较低
Hashtable既不支持Null key也不支持Null value。Hashtable的put()方法的注释中有说明
Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。
HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍
Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的
HashMap为了提高计算效率,将哈希表的大小固定为了2的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。
HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口
12、Object 的 hashcode 方法重写了,equals 方法要不要改?
不需要,Ojbect类中有两个方法equals、hashCode,这两个方法都是用来比较两个对象是否相等的,如果两个对象相等(equal),那么必须拥有相同 的哈希码(hash code)
即使两个对象有相同的哈希值(hash code),他们不一定相等
重写equals()方法就必须重写hashCode(),但重写hashcode方法不一定要重写equals方法
13、Hashmap 线程不安全的出现场景
用ConcurrentHashMap 线程安全
多线程处理时hashmap线程不安全
首先hashmap里这个size没有用volatile关键字修饰,代表这不是一个内存可见的变量,线程操作数据的时候一般是从主存拷贝一个变量副本进行操作,操作完成过后在把size的值写回到主存size的
线程不安全问题应该属于并发问题之一的,属于相对高级的问题了。这个时候的问题已经不仅仅局限于代码层面了,很多时候需要结合JVM一起分析了
14、线上服务 CPU 很高该怎么做?有哪些措施可以找到问题
定位出现问题的堆栈信息排查具体问题
1、top命令:Linux命令。可以查看实时的CPU使用情况。也可以查看最近一段时间的CPU使用情况。
2、ps命令: Linux命令。强大的进程状态监控命令。可以查看进程以及进程中线程的当前CPU使用情况。属于当前状态的采样数据。
3、jstack: Java提供的命令。可以查看某个进程的当前线程栈运行情况。根据这个命令的输出可以定位某个进程的所有线程的当前运行状态、运行代码,以及是否死锁等等。
4、pstack:Linux命令。可以查看某个进程的当前线程栈运行情况
15、JDK 中有哪几个线程池?顺带把线程池讲了个遍
JUC提供了调度器对象Executors来创建线程池,可创建的线程池有四种
1、newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
2、newCachedThreadPool创建一个可缓存的线程池。这种类型的线程池特点是:
1).工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
2).如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
3、newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(我觉得这点是它的特色)。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的 。
4、newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。(这种线程池原理暂还没完全了解透彻)
16、SQL 优化的常见方法有哪些
查询条件减少使用函数,避免全表扫描
减少不必要的表连接
有些数据操作的业务逻辑可以放到应用层进行实现
可以使用with as
尽量避免使用游标,因为游标的效率较差
不要把SQL语句写得太复杂
不能循环执行查询
用 exists 代替 in
表关联关系不要太纠结
查询多用索引列取查,用charindex或者like[0-9]来代替%%
inner关联的表可以先查出来,再去关联leftjoin的表
可以进行表关联数据拆分,即先查出核心数据,再通过核心数据查其他数据,这样会快得多
参考SQL执行顺序进行优化
表关联时取别名,也能提高效率
使用视图,给视图建立索引进行优化
使用数据仓库的形式,建立单独的表存储数据,根据时间戳定期更新数据。将多表关联的数据集中抽取存入一张表中,查询时单表查询,提高了查询效率
对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
19.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描
17、SQL 索引的顺序,字段的顺序
18、查看 SQL 是不是使用了索引?(有什么工具)
在select语句前加上EXPLAIN即可
19、TCP 和 UDP 的区别?TCP 数据传输过程中怎么做到可靠的?
UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种
1)为了保证数据包的可靠传递,发送方必须把已发送的数据包保留在缓冲区;
(2)并为每个已发送的数据包启动一个超时定时器;
(3)如在定时器超时之前收到了对方发来的应答信息(可能是对本包的应答,也可以是对本包后续包的应答),则释放该数据包占用的缓冲区;
(4)否则,重传该数据包,直到收到应答或重传次数超过规定的最大次数为止。
(5)接收方收到数据包后,先进行CRC校验,如果正确则把数据交给上层协议,然后给发送方发送一个累计应答包,表明该数据已收到,如果接收方正好也有数据要发给发送方,应答包也可方在数据包中捎带过去。
20、说下你知道的排序算法吧
常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等
21、查找一个数组的中位数?
通过二分查找法来找中位数
基本思想是:假设ar1[i]是合并后的中位数,那么ar1[i]大于ar1[]中前i-1个数,且大于ar2[]中前j=n-i-1个数。通过ar1[i]和ar2[j]、ar2[j+1]两个数的比较,在ar1[i]的左边或者ar1[i]右边继续进行二分查找。对于两个数组 ar1[] 和ar2[], 先在 ar1[] 中做二分查找。如果在ar1[]中没找到中位数, 继续在ar2[]中查找。
算法流程:
1) 得到数组ar1[]最中间的数,假设下标为i.
2) 计算对应在数组ar2[]的下标j,j = n-i-1
3) 如果 ar1[i] >= ar2[j] and ar1[i] <= ar2[j+1],那么 ar1[i] 和 ar2[j] 就是两个中间元素,返回ar2[j] 和 ar1[i] 的平均值
4) 如果 ar1[i] 大于 ar2[j] 和 ar2[j+1] 那么在ar1[i]的左部分做二分查找(i.e., arr[left ... i-1])
5) 如果 ar1[i] 小于 ar2[j] 和 ar2[j+1] 那么在ar1[i]的右部分做二分查找(i.e., arr[i+1....right])
6) 如果到达数组ar1[]的边界(left or right),则在数组ar2[]中做二分查找
时间复杂度:O(logn)。
二面
22、你有什么问题想问我的吗?
1、自我介绍、工作经历、技术栈
2、项目中你学到了什么技术?
3、微服务划分的粒度?
4、微服务的高可用怎么保证的?
负载均衡与反向代理,隔离,限流,降级,超时与重试,回滚,压力测试与应急预案
5、常用的负载均衡,该怎么用,你能说下吗?
1、http重定向
当http代理(比如浏览器)向web服务器请求某个URL后,web服务器可以通过http响应头信息中的Location标记来返回一个新的URL。这意味着HTTP代理需要继续请求这个新的URL,完成自动跳转。
2、DNS负载均衡
DNS 负责提供域名解析服务,当访问某个站点时,实际上首先需要通过该站点域名的DNS服务器来获取域名指向的IP地址,在这一过程中,DNS服务器完成了域名到IP地址的映射,同样,这样映射也可以是一对多的,这时候,DNS服务器便充当了负载均衡调度器,它就像http重定向转换策略一样,将用户的请求分散到多台服务器上,但是它的实现机制完全不同。
3、反向代理负载均衡
这个肯定大家都有所接触,因为几乎所有主流的Web服务器都热衷于支持基于反向代理的负载均衡。它的核心工作就是转发HTTP请求。
相比前面的HTTP重定向和DNS解析,反向代理的调度器扮演的是用户和实际服务器中间人的角色:
1、任何对于实际服务器的HTTP请求都必须经过调度器
2、调度器必须等待实际服务器的HTTP响应,并将它反馈给用户(前两种方式不需要经过调度反馈,是实际服务器直接发送给用户)
4、IP负载均衡(LVS-NAT)
因为反向代理服务器工作在HTTP层,其本身的开销就已经严重制约了可扩展性,从而也限制了它的性能极限。那能否在HTTP层面以下实现负载均衡呢?
NAT服务器:它工作在传输层,它可以修改发送来的IP数据包,将数据包的目标地址修改为实际服务器地址
5、直接路由(LVS-DR)
NAT是工作在网络分层模型的传输层(第四层),而直接路由是工作在数据链路层(第二层),貌似更屌些。它通过修改数据包的目标MAC地址(没有修改目标IP),将数据包转发到实际服务器上,不同的是,实际服务器的响应数据包将直接发送给客户羰,而不经过调度器
6、IP隧道(LVS-TUN)
基于IP隧道的请求转发机制:将调度器收到的IP数据包封装在一个新的IP数据包中,转交给实际服务器,然后实际服务器的响应数据包可以直接到达用户端。目前Linux大多支持,可以用LVS来实现,称为LVS-TUN,与LVS-DR不同的是,实际服务器可以和调度器不在同一个WANt网段,调度器通过 IP隧道技术来转发请求到实际服务器,所以实际服务器也必须拥有合法的IP地址。
总体来说,LVS-DR和LVS-TUN都适合响应和请求不对称的Web服务器,如何从它们中做出选择,取决于你的网络部署需要,因为LVS-TUN可以将实际服务器根据需要部署在不同的地域,并且根据就近访问的原则来转移请求,所以有类似这种需求的,就应该选择LVS-TUN。
6、网关能够为后端服务带来哪些好处?
后端服务器可以专心处理业务请求,节省了大量连接管理的开销
7、Spring Bean 的生命周期
8、xml 中配置的 init、destroy 方法怎么可以做到调用具体的方法?
9、反射的机制
大家都知道,要让Java程序能够运行,那么就得让Java类要被Java虚拟机加载。Java类如果不被Java虚拟机加载,是不能正常运行的。现在我们运行的所有的程序都是在编译期的时候就已经知道了你所需要的那个类的已经被加载了。
Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类。这样的特点就是反射
反射机制通过void setAccessible(boolean flag)方法可以得到一个类的private的方法和属性,使用这些private的方法和属性
10、Object 类中的方法
1,构造函数
2,hashCode和equale函数用来判断对象是否相同,
3,wait(),wait(long),wait(long,int),notify(),notifyAll()
4,toString()和getClass,
5,clone()
6,finalize()用于在垃圾回收
11、hashcode 和 equals 方法常用地方
12、对象比较是否相同
equals通常用来比较两个对象的内容是否相等,==用来比较两个对象的地址是否相等
13、hashmap put 方法存放的时候怎么判断是否是重复的
先比较key的hashCode,再比较相等或equals的,所以重写hashCode()和equals()方法即可实现添加重复元素。
14、Object toString 方法常用的地方,为什么要重写该方法
常用在对象模型类
因为假如User是一个用户的对象,如果User.toString();结果是不正常的,因为User对象中可能有多个属性,如年龄,姓名等,这个toString后无法知道具体的是那个属性转换为字符串
15、Set 和 List 区别?
Set(集):集合中的对象不按特定方式排序,并且没有重复对象。它的有些实现类能对集合中的对象按特定方式排序。
List(列表):集合中的对象按索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象。
16、ArrayList 和 LinkedList 区别
ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构
ArrayList 继承AbstractList
LinkedList 继承AbstractSequentialList
ArrayList 采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺点就是插入删除时非常麻烦
LinkedList 采用的将对象存放在独立的空间中,而且在每个空间中还保存下一个链接的索引 但是缺点就是查找非常麻烦 要丛第一个索引开始
17、如果存取相同的数据,ArrayList 和 LinkedList 谁占用空间更大?
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针
对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据,若要从数组中删除或插入某一个对象,需要移动后段的数组元素,从而会重新调整索引顺序,调整索引顺序会消耗一定的时间,相反,LinkedList是使用链表实现的,若要从链表中删除或插入某一个对象,只需要改变前后对象的引用即可
18、Set 存的顺序是有序的吗?
无序
Set是Map的一个马甲,主要逻辑都交给Map实现
19、常见 Set 的实现有哪些?
HashSet
LinkedHashSet
TreeSet
20、TreeSet 对存入对数据有什么要求呢?
TreeSet集合是用来对象元素进行排序的,同样他也可以保证元素的唯一
21、HashSet 的底层实现呢?
22、TreeSet 底层源码有看过吗?
TreeSet的底层实现是TreeMap
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
23、HashSet 是不是线程安全的?为什么不是线程安全的?
说白了,HashSet就是限制了功能的HashMap,所以了解HashMap的实现原理
24、Java 中有哪些线程安全的 Map?
Concurrenthashmap
25、Concurrenthashmap 是怎么做到线程安全的?
ConcurrentHashMap的大部分操作和HashMap是相同的,例如初始化,扩容和链表向红黑树的转变等。但是,在ConcurrentHashMap中,大量使用了U.compareAndSwapXXX
的方法,这个方法是利用一个CAS算法实现无锁化的修改值的操作,他可以大大降低锁代理的性能消耗。这个算法的基本思想就是不断地去比较当前内存中的变量值与你指定的
一个变量值是否相等,如果相等,则接受你指定的修改的值,否则拒绝你的操作。因为当前线程中的值已经不是最新的值,你的修改很可能会覆盖掉其他线程修改的结果。这一
点与乐观锁,SVN的思想是比较类似的。
同时,在ConcurrentHashMap中还定义了三个原子操作,用于对指定位置的节点进行操作。这三种原子操作被广泛的使用在ConcurrentHashMap的get和put等方法中,
正是这些原子操作保证了ConcurrentHashMap的线程安全。
在ConcurrentHashMap没有出现以前,jdk使用hashtable来实现线程安全,但是hashtable是将整个hash表锁住,所以效率很低下。
ConcurrentHashMap将数据分别放到多个Segment中,默认16个,每一个Segment中又包含了多个HashEntry列表数组,
对于一个key,需要经过三次hash操作,才能最终定位这个元素的位置,这三次hash分别为:
对于一个key,先进行一次hash操作,得到hash值h1,也即h1 = hash1(key);
将得到的h1的高几位进行第二次hash,得到hash值h2,也即h2 = hash2(h1高几位),通过h2能够确定该元素的放在哪个Segment;
将得到的h1进行第三次hash,得到hash值h3,也即h3 = hash3(h1),通过h3能够确定该元素放置在哪个HashEntry。
每一个Segment都拥有一个锁,当进行写操作时,只需要锁定一个Segment,而其它Segment中的数据是可以访问的。
26、HashTable 你了解过吗?
Hashtable既不支持Null key也不支持Null value。Hashtable的put()方法的注释中有说明
Hashtable是线程安全的,
Hashtable是线程安全的,它的每个方法中都加入了Synchronize方法,效率比较低
Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。
Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的。
27、如何保证线程安全问题?
28、synchronized、lock
synchronized是java中的一个关键字,也就是说是Java语言内置的特性
如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
2)线程执行发生异常,此时JVM会让线程自动释放锁
那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。
因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到
再举个例子:当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。
但是采用synchronized关键字来实现同步的话,就会导致一个问题:
如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。
因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。
另外,通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的
29、volatile 的原子性问题?为什么 i++ 这种不支持原子性?从计算机原理的设计来讲下不能保证原子性的原因
30、happens before 原理
31、cas 操作
java.util.concurrent包中借助CAS实现了区别于synchronized同步锁的一种乐观锁
cas是比较并交换算法
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做
JDK提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作。
32、lock 和 synchronized 的区别?
1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5)Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
类别
synchronized
Lock
存在层次
Java的关键字,在jvm层面上
是一个类
锁的释放
1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁
在finally中必须释放锁,不然容易造成线程死锁
锁的获取
假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待
分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待
锁状态
无法判断
可以判断
锁类型
可重入 不可中断 非公平
可重入 可判断 可公平(两者皆可)
性能
少量同步
大量同步
33、公平锁和非公平锁
公平和非公平锁的队列都基于锁内部维护的一个双向链表,表结点Node的值就是每一个请求当前锁的线程。公平锁则在于每次都是依次从队首取值
非公平锁在等待锁的过程中, 如果有任意新的线程妄图获取锁,都是有很大的几率直接获取到锁的
(在ReentrantLock中很明显可以看到其中同步包括两种,分别是公平的FairSync和非公平的NonfairSync。公平锁的作用就是严格按照线程启动的顺序来执行的,不允许其他线程插队执行的;而非公平锁是允许插队的。
默认情况下ReentrantLock是通过非公平锁来进行同步的,包括synchronized关键字都是如此,因为这样性能会更好。因为从线程进入了RUNNABLE状态,可以执行开始,到实际线程执行是要比较久的时间的。而且,在一个锁释放之后,其他的线程会需要重新来获取锁。其中经历了持有锁的线程释放锁,其他线程从挂起恢复到RUNNABLE状态,其他线程请求锁,获得锁,线程执行,这一系列步骤。如果这个时候,存在一个线程直接请求锁,可能就避开挂起到恢复RUNNABLE状态的这段消耗,所以性能更优化)
34、Java 读写锁
35、读写锁设计主要解决什么问题?
多线程,
读操作可以共享,写操作是排他的,读可以有多个在读,写只有唯一个在写,同时写的时候不允许读
解决了读和读可以同时进行,读和写不能同时进行,写和写不能同时进行
36、你项目除了写 Java 代码,还有前端代码,那你知道前端有哪些框架吗?
Vue layer react element
37、MySQL 分页查询语句
LIMIT [offset,] rows
offset指定要返回的第一行的偏移量,rows第二个指定返回行的最大数目
38、MySQL 事务特性和隔离级别
事务的基本要素(ACID)
1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
二、事务的并发问题
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读
MySQL事务隔离级别
事务隔离级别
脏读
不可重复读
幻读
读未提交(read-uncommitted)
不可重复读(read-committed)
可重复读(repeatable-read)
串行化(serializable)
39、不可重复读会出现在什么场景?
40、sql having 的使用场景
如果需要对组函数的结果作为条件,那么不能使用where子句,必须使用having子句
41、前端浏览器地址的一个 http 请求到后端整个流程是怎么样?
能够说下吗?
域名解析 --> 发起TCP的3次握手 --> 建立TCP连接后发起http请求 -->服务器响应http请求,浏览器得到html代码 -->浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户
42、http 默认端口,https 默认端口
HTTP协议代理服务器常用端口号:80/8080/3128/8081/9080
HTTPS服务器,默认的端口号为443/tcp 443/udp
43、DNS 你知道是干嘛的吗?
DNS是指:域名服务器(Domain Name Server)。在Internet上域名与IP地址之间是一一对应的,域名虽然便于人们记忆,但机器之间只能互相认识IP地址,它们之间的转换工作称为域名解析,域名解析需要由专门的域名解析服务器来完成,DNS就是进行域名解析的服务器
44、你们开发用的 ide 是啥?你能说下 idea 的常用几个快捷键吧?
45、代码版本管理你们用的是啥?
46、git rebase 和 merge 有什么区别?
47、你们公司加班多吗?
注:有些问题待补充。