阿里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和CAS


1.StringBuffer 和 StringBuilder 的区别

可变性。String 不可变,StringBuilder 与 StringBuffer 是可变的

String 类中使用只读字符数组保存字符串, private final char value [], 所以是不可变的(Java 9 中底层把 char 数组换成了 byte 数组,占用更少的空间)。

StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串, char [] value ,这两种对象都是可变的。

线程安全性。String 和 StringBuffer 是线程安全的,StringBuilder 是非线程安全的。

String 线程安全 是因为其对象是不可变的, StringBuffer 线程安全 是因为对方法加了同步锁或者对调用的方法加了同步锁。

StringBuilder 并没有对方法进行加同步锁,所以是 非线程安全的 。

性能

String 的性能较差,因为每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。 而 StringBuffer/StringBuilder 性能更高 ,是因为 每次都是对对象本身进行操作 ,而 不是生成新的对象并改变对象引用 。一般情况下 StringBuilder 相比 StringBuffer 可获得 10%~15% 左右的性能提升。

点评

如果要操作少量的数据用 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 采用 HashEntry+Segment 的结构,ConcurrentHashMap 里一共 16 个 Segment,Segment 是可重入锁 ReentrantLock 的子类,每个 Segment 对应一个 HashEntry 键值对数组。当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 锁 ,因此,多线程访问容器里不同 Segment 的数据,就不会存在锁竞争,从而提升并发性能。

JDK1.8 中则摒弃了 Segment 的概念, 并发控制使用 synchronized 和 CAS 来操作 ,虽然在 JDK1.8 中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本。来看看核心的 put 方法。

补充

CAS 原子语义来处理加减等操作,CAS 全称 Compare And Swap(比较与交换) ,通过判断内存某个位置的值是否与预期值相等,如果相等则进行值更新。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 (long timeout, TimeUnit unit),

另一种是 lockInterruptibly () 放代码块中,调用 interrupt () 方法进行中断;

(4) 加锁是否公平

Synchronized 是非公平锁

ReentrantLock 默认非公平锁

可以在构造方法传入 boolean 值,true 代表公平锁,false 代表非公平锁;

5.SpringMVC的运行原理

阿里p8架构师,总结出JAVA面试十一问,你确定都会?

6.分布式锁怎么实现

CAP理论

即满足 一致性(Consistency) 、 可用性(Availability) 分区容错性(Partition tolerance)

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监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。

阿里p8架构师,总结出JAVA面试十一问,你确定都会?

7.BIO 和 NIO区别

BIO(Blocking IO)阻塞IO

NIO(Non-Blocking IO)非阻塞IO

共同点:两者都是同步操作。即必须先进行IO操作后才能进行下一步操作。

不同点:

BIO多线程对某资源进行 IO操作时会出现阻塞 ,即一个线程进行IO操作完才会通知另外的IO操作线程,必须等待。

NIO多线程对某资源进行IO操作时会把资源先操作 至内存缓冲区 。然后询问是否IO操作就绪,是则进行IO操作,否则进行下一步操作,然后不断的 轮询是否IO操作就绪 ,直到iIO操作就绪后进行相关操作。

8.new 一个对象,JVM 里面都干了啥

先是加载,验证,准备,解析,初始化

阿里p8架构师,总结出JAVA面试十一问,你确定都会?

9.volatile 关键字

从原子性,可见性,指令重排三个方面说了

1.保证可见性:线程之间可见性(及时通知)

2.不保证原子性

3.禁止指令重排

10.Synchronized 关键字在 1.6 做了哪些优化

从锁消除,锁粗化,偏向锁,轻量级锁,重量级锁解锁了一遍。

1.适应自旋锁:为了减少线程状态改变带来的消耗 不停地执行当前线程

2.锁消除:不可能存在共享数据竞争的锁进行消除

3.锁粗化: 将连续的加锁 精简到只加一次锁

4.轻量级锁: 无竞争条件下 通过CAS消除同步互斥

5.偏向锁:无竞争条件下 消除整个同步互斥,连CAS都不操作。

11.AQS和CAS

CAS

CAS(Compare And Swap),即比较并交换。 是解决多线程并行情况下使用锁造成性能损耗的一种机制, 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(共享):多个线程可同时执行,如 Semaphore/CountDownLatch/CyclicBarrier 等。

此外,也可以通过 AQS 来自定义同步器,自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队 / 唤醒出队等),AQS 已经在顶层实现好了。

相信兄弟盟已经知道了Java面试需要准备些什么,点关注,不迷路,转发+关注阿远每天分享不同的Java知识以及面试要点。

展开阅读全文

更新时间:2024-09-05

标签:死锁   都会   阿里   数据结构   数组   节点   线程   公平   对象   条件   操作   资源

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top