面试官:欢迎你来面试,第一轮我们先来聊聊Java核心知识。首先,说说Java中的多态是怎么实现的?
王铁牛:多态就是一个对象可以有多种形态嘛。通过方法重写和接口实现就能实现多态。比如一个父类定义了一个方法,子类重写这个方法,然后用子类对象调用这个方法的时候,就会表现出不同的行为。
面试官:回答得不错。那再问你,什么是Java的内存模型?
王铁牛:Java内存模型就是一种规范,规定了Java程序中各个变量的访问规则。它定义了主内存和线程本地内存,变量在主内存中存储,线程操作变量时会先把变量拷贝到自己的本地内存,操作完再写回主内存。
面试官:嗯,理解得挺到位。最后一个问题,String类为什么是不可变的?
王铁牛:因为String类内部用final修饰了字符数组,一旦初始化就不能再修改,所以它是不可变的。
面试官:好,第一轮表现不错。接下来第二轮,聊聊JUC相关的。首先,讲讲CountDownLatch的作用和使用场景。
王铁牛:CountDownLatch可以让一个或多个线程等待其他线程完成一组操作后再继续执行。比如多个线程一起做一个任务,等所有线程都完成了各自的部分,再一起进行下一步操作,就可以用CountDownLatch。
面试官:那CyclicBarrier和CountDownLatch有什么区别?
王铁牛:CyclicBarrier是多个线程相互等待,直到所有线程都到达一个屏障点,然后再一起继续执行,而且它可以复用。CountDownLatch是一个线程等待其他线程完成操作后再继续,不能复用。
面试官:不太准确啊。再问,线程池的核心参数都有哪些?
王铁牛:有corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。
面试官:第二轮整体一般。下面进入第三轮,说说JVM的垃圾回收算法。
王铁牛:有标记清除算法、标记整理算法、复制算法、分代收集算法。
面试官:具体讲讲标记清除算法的优缺点。
王铁牛:优点就是简单,缺点是会产生内存碎片。
面试官:还有,CMS垃圾回收器的工作过程是怎样的?
王铁牛:这个……不太清楚。
面试官:好,面试就到这里,回去等通知吧。
答案:
- Java多态的实现:多态主要通过方法重写和接口实现。在继承体系中,子类重写父类的方法,当使用子类对象调用这个被重写的方法时,就会表现出与父类不同的行为,这就是多态的体现。例如,父类有一个方法
void method(),子类重写为void method(),当用子类对象调用method方法时,实际执行的是子类重写后的逻辑。 - Java内存模型:Java内存模型规定了Java程序中变量的访问规则。它定义了主内存和线程本地内存,变量存储在主内存中,线程操作变量时,会先将变量拷贝到自己的本地内存,操作完成后再写回主内存。这样做是为了保证多线程环境下变量的可见性和一致性。
- String类不可变的原因:String类内部使用
final修饰字符数组,一旦初始化,字符数组的引用不能再指向其他数组,并且数组内的元素也不能被修改,所以String是不可变的。这使得它在多线程环境下更安全,并且可以被缓存复用。 - CountDownLatch的作用和使用场景:CountDownLatch用于让一个或多个线程等待其他线程完成一组操作后再继续执行。比如在多线程处理任务时,主线程需要等待所有子线程都完成各自的任务后,再进行下一步操作,就可以使用CountDownLatch。例如,有多个线程负责读取不同文件的数据,主线程需要等待所有线程读取完数据后,再对数据进行汇总处理。
- CyclicBarrier和CountDownLatch的区别:
- CountDownLatch:是一个线程等待其他线程完成操作后再继续。它通过
countDown方法减少计数,当计数为0时,等待的线程继续执行。它主要用于一次性的同步场景,比如一个线程等待多个子线程完成任务后再执行后续操作,并且不能复用。 - CyclicBarrier:是多个线程相互等待,直到所有线程都到达一个屏障点,然后再一起继续执行,并且可以复用。它通过
await方法使线程等待,当所有线程都调用await方法到达屏障点时,所有线程才会继续执行。适用于多个线程需要相互协作完成某个阶段任务的场景,例如多个运动员在接力比赛中,每个运动员完成自己的一段赛程后,等待所有运动员都完成该段赛程,再一起进入下一段赛程。
- CountDownLatch:是一个线程等待其他线程完成操作后再继续。它通过
- 线程池的核心参数:
- corePoolSize:线程池的核心线程数,当提交的任务数小于corePoolSize时,线程池会创建新线程来执行任务。
- maximumPoolSize:线程池允许的最大线程数,当提交的任务数大于corePoolSize且workQueue已满时,会创建新线程执行任务,但线程数不能超过maximumPoolSize。
- keepAliveTime:线程池中非核心线程的存活时间,当线程空闲时间超过keepAliveTime时,非核心线程会被销毁。
- unit:keepAliveTime的时间单位。
- workQueue:线程池中的任务队列,用于存储提交的任务,当线程池线程繁忙时,任务会被放入workQueue中等待执行。
- threadFactory:用于创建线程池中的线程,可自定义线程的创建方式,如线程名、优先级等。
- handler:当线程池中的线程数达到maximumPoolSize且workQueue已满时,新提交的任务会由handler来处理,常见的handler有AbortPolicy(直接抛出异常)、CallerRunsPolicy(调用者运行)、DiscardPolicy(丢弃任务)、DiscardOldestPolicy(丢弃最旧的任务)。
- JVM垃圾回收算法之标记清除算法:
- 标记阶段:遍历所有对象,标记出存活的对象。
- 清除阶段:清除所有未被标记的对象,释放其占用的内存空间。
- 优点:简单易懂,实现起来相对容易。
- 缺点:会产生大量不连续的内存碎片,导致后续分配大对象时可能无法找到连续的内存空间,从而提前触发垃圾回收。
- CMS垃圾回收器的工作过程:
- 初始标记阶段:标记出与GC Roots直接相连的对象,速度很快,因为只需要标记这些直接相关的对象。
- 并发标记阶段:与应用程序线程并发执行,标记出所有存活的对象,这个阶段不会停顿应用程序,但会消耗一定的CPU资源。
- 重新标记阶段:修正并发标记期间因对象变动而产生的标记变动,这个阶段会有短暂的停顿。
- 并发清除阶段:与应用程序线程并发执行,清除死亡对象,释放内存空间。CMS垃圾回收器的主要目的是减少垃圾回收时的停顿时间,适合对停顿时间要求较高的应用场景。