校招实习面试系列,每日10题,快速学习Java高级篇

Posted 陆海潘江小C

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了校招实习面试系列,每日10题,快速学习Java高级篇相关的知识,希望对你有一定的参考价值。

【校招实习面试系列,每日10题,快速学习】Java高级篇


备战春招,校招实习面经分享,每天10到面试题,轻松拿Java开发工程师offer~~

本系列文章包括Java、算法、计算机网络、数据库、操作系统等等,本篇介绍Java的高级特性和应用。

传送门:

  1. 【亲身面经分享,校招实习面试系列】每日10题,快速学习(Java基础篇)

1、ThreadLocal的应用场景有哪些?

  • 用作保存每个线程独享的对象,为每个线程创建一个副本,自身线程的修改不会影响其他线程的副本,保证了线程安全。

  • 每个线程独立保存的信息,以便其他方法获取该信息。每个线程获取的信息可能都是不一样的,前者执行方法保存信息,后者可通过ThreadLocal直接获取,避免传参。类似全局变量

上一篇也提到在保证同步的方法中,ThreadLocal可作为程序局部变量来保证同步。

通常,面试会逐步深入一个知识点,问到ThreadLocal时,也许会更深入问接下来这个问题。

2、ThreadLocal为什么存在内存泄露的问题,如何解决?

很明显,这需要比较扎实的知识,阅读过它的源码,或者了解特性才能够回答。

  • ThreadLocal它内部其实是一个ThreadLocalMap,这个ThreadLocalMap的key指向当前用户执行的线程对象。

    Thread t = Thread.currentThread()
    
  • ThreadLocalMap.set方法其实tab[i] = new Entry(key, value)。Entry是一个弱引用。

我们知道java弱引用,只要JVM发生GC时候就会被回收,那么问题来啦,ThreadLocalMap引用栈被回收但它的VALUE且一直存在。在生产环境中,一个线程也许不间断运行,势必会造成内存泄漏问题。

由此可知,Map结构的key-value模式,就需要同步回收键和值的空间,来避免内存泄漏。

【解决方法】

ThreadLocal采用弱引用,但还是存在内存泄漏,因为ThreadLocal被回收,key的值变成null,则导致整个value再也无法被访问到。必须调用remove方法回收value空间。

3、AQS是什么?

AQS(AbstractQueuedSynchronizer)即队列同步器,是用来构建锁或者其他同步组件的基础框架,公平锁严格FIFO队列来获取线程的排队工作。

优势:AQS实现Synchronizer减少了大量细节,如等待线程FIFO队列,应付多个竞争点。AQS的synchronizer只会在一点上发送阻塞,降低上下文切换的开销。所有的juc中基于AQS构建的synchronizer性能都较高

总之,AQS是实现同步的类基础框架,juc包下的很多锁,比如ReentrantLock。

AQS的类图:

4、CAS是什么?

上个问题,基于AQS同步器实现的锁中,实现同步的算法很多都采用CAS。在Java.util.ConCurrent包里面很多都是用CAS来处理同步的问题,而不是直接来个synchronized来修饰。

CAS即Compare And Swap,是一种比较并交换算法。使用CAS算法实现同步的锁,通常都是乐观锁。

CAS原理:

CAS有三个操作值:内存值V、预期值A与修改值B。只有当预期值A与内存值V相同时,才会将内存值替换成B。否则,会进入下一轮循环中。

乐观锁在拿数据的时候不会去加锁,但是会在更新的时候判断此期间内有没有别的线程修改过数据。

还有一个随之而来的经典问题:ABA问题

ABA问题:

是指当乐观锁拿到数据后,经过判断在上次到此时这段时间内,没有别的线程修改过该数据,此时数据为A。

而实际上,在此期间,存在一个线程将数据修改为B,然后又修改为A,掩盖了该数据被修改过的事实。

这就是ABA问题。

解决方法:数据标识上版本号或时间戳,避免ABA问题。

5、基于CAS实现的锁和synchronized有什么区别?

前一个题目已经讲到,基于CAS实现的是乐观锁。

乐观锁在拿数据的时候不会去加锁,但是会在更新的时候判断此期间内有没有别的线程修改过数据。

而synchronized关键字实现的是一种重量级锁,是因为在互斥状态下,没有得到锁的线程会被挂起阻塞,而挂起线程和恢复线程的操作都需要转入内核态中完成。系统开销很大。

我们经常使用的原子变量Atomic,它的实现原理是CAS+自旋

CAS乐观锁保证原子性,通过自旋保证当次修改的最终修改成功,通过降低锁粒度(多段锁)增加并发性能

6、什么是自旋锁?

自旋锁是一种基于CAS的锁,获取锁的线程不会被阻塞,而是循环的去获取锁。

指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

自旋锁的好处是线程不用挂起阻塞,减小一定的系统开销。不足之处是可能会遇到无限循环等待,这样产生的开销也挺大。

7、你知道ReentrantLock的特性吗?

ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁。它的性能比较好也是因为避免了使线程进入内核态的阻塞状态

有了前面几个问题的铺垫,确定ReetantLock锁属于哪种类型,使用什么同步方法,这里就很好理解了。

  • 等待可中断:指当持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,改为处理其他事情,这对于处理执行时间非常长的同步块很有帮助。

  • 公平锁:是指多个线程等待同一个锁时,必须按照申请锁的时间顺序排队获取;非公平锁则是任何一个等待锁的线程都有机会获得锁。

    synchronized是非公平的,ReentrantLock默认也是非公平的,但可以通过构造函数的bool值设置公平锁,然而,公平锁也会导致性能急剧下降

  • 锁绑定多个条件:ReentrantLock对象可以同时绑定多个condition对象。在synchronized中,锁对象的wait和notify或notifyAll配合实现一个条件,多个条件时就需要增加锁;而ReentrantLock只需多次调用newCondition方法。

8、volatile关键字的原理是什么?

volatile修饰的变量在每次被线程访问时,都强迫从主内存中重读该变量的值。而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存,这样任何时刻,不同的线程总能看到该变量的最新值

因此,volatile具有可见性,指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。

9、HashMap为何线程不安全?

HashMap这个很常用的数据结构是不是线程安全的,这一点众所周知,为何线程不安全呢?

HashMap的底层是基于数组和链表两种数据结构实现的,因此也带来了问题。

  • jdk7:链表使用头插法,两个线程进行扩容时候,出现链表环、数据丢失、数据覆盖情况

  • jdk8:尾插法,两个线程进行put操作,判断hash碰撞时,cpu时间片到,导致A、B线程插入数据出现覆盖

10、可重入函数和线程安全的关系是什么?

可重入(reentrant),原来英文单词是这样,和我们熟悉的锁ReetrantLock前缀一致,因此这是可重入锁。

可重入函数的意思就是:

简单来说就是可以被中断的函数,可以由多于一个任务并发使用,而不必担心数据错误,使用本地变量或者全局变量来保护自己的数据。

同一线程反复进入同步块不会出现自己把自己锁死的情况。

到这里,也是有点明白可重入函数跟线程安全为啥能扯上关系了。

在一个线程中,在此情况下如果函数是可重入的,那么就称这个函数是线程安全的。



欢迎“一键三连”哦,点赞加关注,收藏不迷路!

每天进步亿点点,距离Java工程师更近一步啦,我们下篇见!(⊙ᗜ⊙)

公众号同步更新哦,习惯阅读公众号的伙伴们可以关注下面我的公众号呀!


本篇内容首发我的CSDN博客:https://csdn-czh.blog.csdn.net/article/details/123112957

以上是关于校招实习面试系列,每日10题,快速学习Java高级篇的主要内容,如果未能解决你的问题,请参考以下文章

亲身面经分享,校招实习面试系列每日10题,快速学习(Java基础篇)

亲身面经分享,校招实习面试系列每日10题,快速学习(Java基础篇)

校招实习面试实战,顺丰科技Java工程师面试复盘总结

Java实习生每日10道面试题打卡!

Java实习生每日10道面试题打卡!

校招实习面试系列你知道网络中4类IO模型是什么吗?我的朋友如此优秀回答