阿里p8架构师,总结出JAVA面试十一问,你确定都会?
最近金三银四,拉开了帷幕,各个大厂也已经开始抢人,各位小伙伴也开始了万人抢过独木桥的战争。在这里阿远为大家搜集了一些java面试题,希望对大家有所帮助。
文章目录1。StringBuffer和StringBuilder的区别
2。一般的有死锁怎么形成的,怎么解决死锁
3。HashMap,ConcurrentHashMap,LinkedHashMap的区别
4。synchronized和ReentrantLock的异同
5。SpringMVC的运行原理
6。分布式锁怎么实现
7。BIO和NIO区别
8。new一个对象,JVM里面都干了啥
9。volatile关键字
10。Synchronized关键字在1。6做了哪些优化
11。AQS和CAS1。StringBuffer和StringBuilder的区别
可变性。String不可变,StringBuilder与StringBuffer是可变的。String类中使用只读字符数组保存字符串,privatefinalcharvalue〔〕,所以是不可变的(Java9中底层把char数组换成了byte数组,占用更少的空间)。
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char〔〕value,这两种对象都是可变的。
线程安全性。String和StringBuffer是线程安全的,StringBuilder是非线程安全的。String线程安全是因为其对象是不可变的,StringBuffer线程安全是因为对方法加了同步锁或者对调用的方法加了同步锁。
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
性能
String的性能较差,因为每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象。而StringBufferStringBuilder性能更高,是因为每次都是对对象本身进行操作,而不是生成新的对象并改变对象引用。一般情况下StringBuilder相比StringBuffer可获得1015左右的性能提升。
点评:
如果要操作少量的数据用String;单线程操作字符串缓冲区下操作大量数据StringBuilder;多线程操作字符串缓冲区下操作大量数据StringBuffer;2。一般的有死锁怎么形成的,怎么解决死锁
什么是线程死锁?
死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。
死锁产生的条件是什么?
(1)互斥条件:该资源任意一个时刻只由一个线程占用;
(2)请求与保持条件:一个线程进程因请求资源而阻塞时,对已获得的资源保持不放;
(3)不剥夺条件:线程进程已获得的资源在末使用完之前不能被其他线程进程强行剥夺,只有自己使用完毕后才释放资源;
(4)循环等待条件:若干线程进程之间形成一种头尾相接的循环等待资源关系。
如何避免线程死锁?
针对死锁产生的条件进行一一拆解:
(1)破坏互斥条件:无法破坏,因为使用锁的本意就是想让它们互斥的(临界资源需要互斥访问);
(2)破坏请求与保持条件:一次性申请所有的资源;
(3)破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源;
(4)破坏循环等待条件:按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件(最常用)。3。HashMap,ConcurrentHashMap,LinkedHashMap的区别1。HashMap是线程不安全的,HashTable是线程安全的。
2。HashMap的键需要重新计算对象的hash值,而HashTable直接使用对象的hashCode。
3。HashMap的值和键都可以为null,HashTable的值和键都不能为null。
4。HashMap的数组的默认初始化大小为16,HashTable为11;HashMap扩容时会扩大两倍,HashTable扩大两倍1;
LinkedHashMap维护一个双链表,可以将里面的数据按写入的顺序读出
基础特性不同:
HashMap的key和value可以为null,ConcurrentHashMap的key和value不能为null。
内部数据结构不同:
HashMap在JDK1。7中采用的数据结构是数组链表,在JDK1。8中采用的数据结构是数组链表红黑二叉树;
ConcurrentHashMap在JDK1。7中采用的数据结构是分段的数组链表,JDK1。8的内部数据结构采用的数据结构是数组链表红黑二叉树(同HashMap一致)。
线程安全不同:
HashMap是非线程安全的;
ConcurrentHashMap是线程安全的;
ConcurrentHashMap
JDK1。7中,ConcurrentHashMap采用HashEntrySegment的结构,ConcurrentHashMap里一共16个Segment,Segment是可重入锁ReentrantLock的子类,每个Segment对应一个HashEntry键值对数组。当对HashEntry数组的数据进行修改时,必须首先获得对应的Segment锁,因此,多线程访问容器里不同Segment的数据,就不会存在锁竞争,从而提升并发性能。
JDK1。8中则摒弃了Segment的概念,并发控制使用synchronized和CAS来操作,虽然在JDK1。8中还能看到Segment的数据结构,但是已经简化了属性,只是为了兼容旧版本。来看看核心的put方法。补充
CAS原子语义来处理加减等操作,CAS全称CompareAndSwap(比较与交换),通过判断内存某个位置的值是否与预期值相等,如果相等则进行值更新。CAS是内部是通过Unsafe类实现,而Unsafe类的方法都是native的,在JNI里是借助于一个CPU指令完成的,属于原子操作。4。synchronized和ReentrantLock的异同
1。相同点:Lock能完成synchronized所实现的所有功能;
2。不同点:Lock有比synchronized更精确的线程语义和更好的性能,而且不强制性的要求一定要获得锁。synchronized会自动释放锁,而Lock则要求手工释放。更具体地来说,有以下差异:
(1)含义不同
Synchronized是关键字,属于JVM层面,底层是通过monitorenter和monitorexit完成,依赖于monitor对象来完成;
Lock是java。util。concurrent。locks。lock包下的,是JDK1。5以后引入的新API层面的锁;
(2)使用方法不同
Synchronized不需要用户手动释放锁,代码完成之后系统自动让线程释放锁;ReentrantLock需要用户手动释放锁,没有手动释放可能导致死锁;
(3)等待是否可以中断
Synchronized不可中断,除非抛出异常或者正常运行完成;
ReentrantLock可以中断。
一种是通过tryLock(longtimeout,TimeUnitunit),
另一种是lockInterruptibly()放代码块中,调用interrupt()方法进行中断;
(4)加锁是否公平
Synchronized是非公平锁;
ReentrantLock默认非公平锁,
可以在构造方法传入boolean值,true代表公平锁,false代表非公平锁;5。SpringMVC的运行原理
6。分布式锁怎么实现
CAP理论
即满足一致性(Consistency)、可用性(Availability)和分区容错性(Partitiontolerance)
1。基于数据库。
基于数据库的实现方式的核心思想是:在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。
2。基于缓存环境,redis,memcache等。(1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
(2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。
3。基于zookeeper。(1)创建一个目录mylock;
(2)线程A想获取锁就在mylock目录下创建临时顺序节点;
(3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。
7。BIO和NIO区别
BIO(BlockingIO)阻塞IO
NIO(NonBlockingIO)非阻塞IO共同点:两者都是同步操作。即必须先进行IO操作后才能进行下一步操作。
不同点:
BIO多线程对某资源进行IO操作时会出现阻塞,即一个线程进行IO操作完才会通知另外的IO操作线程,必须等待。
NIO多线程对某资源进行IO操作时会把资源先操作至内存缓冲区。然后询问是否IO操作就绪,是则进行IO操作,否则进行下一步操作,然后不断的轮询是否IO操作就绪,直到iIO操作就绪后进行相关操作。8。new一个对象,JVM里面都干了啥
先是加载,验证,准备,解析,初始化
9。volatile关键字
从原子性,可见性,指令重排三个方面说了
1。保证可见性:线程之间可见性(及时通知)
2。不保证原子性
3。禁止指令重排10。Synchronized关键字在1。6做了哪些优化
从锁消除,锁粗化,偏向锁,轻量级锁,重量级锁解锁了一遍。
1。适应自旋锁:为了减少线程状态改变带来的消耗不停地执行当前线程
2。锁消除:不可能存在共享数据竞争的锁进行消除
3。锁粗化:将连续的加锁精简到只加一次锁
4。轻量级锁:无竞争条件下通过CAS消除同步互斥
5。偏向锁:无竞争条件下消除整个同步互斥,连CAS都不操作。11。AQS和CASCAS
CAS(CompareAndSwap),即比较并交换。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。CAS有效地说明了
我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。
AQS
AQS的原理
抽象队列同步器
AQS(AbstractQueuedSynchronizer)核心思想是,如果被请求的资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态;如果被请求的资源被占用,则需要一套线程阻塞等待以及唤醒分配的机制,该机制基于一个FIFO(先进先出)的等待队列实现。
AQS的应用
作为一个用来构建锁和同步器的框架,AQS能简单且高效地构造出大量同步器,事实上java。util。concurrent。concurrent包内许多并发类都是基于AQS构建。这些同步器从资源共享方式的方式来看,可以分为两类:
(1)Exclusive(独占):只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁:
A、公平锁:按照线程在队列中的排队顺序,先到者先拿到锁;
B、非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的。
(2)Share(共享):多个线程可同时执行,如SemaphoreCountDownLatchCyclicBarrier等。
此外,也可以通过AQS来自定义同步器,自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队唤醒出队等),AQS已经在顶层实现好了。
相信兄弟盟已经知道了Java面试需要准备些什么,点关注,不迷路,转发关注阿远每天分享不同的Java知识以及面试要点。