中卫市网站建设_网站建设公司_产品经理_seo优化
2025/12/18 10:43:41 网站建设 项目流程

面试官:Java多线程和JUC你懂吗?谢飞机:我飞过!——互联网大厂技术面试搞笑实录(一)

场景:某互联网大厂会议室,阳光明媚但气氛紧张。面试官面无表情地翻着简历,对面坐着一位自称“精通全栈”的求职者——谢飞机。


第一轮提问:Java 多线程基础

面试官:谢飞机是吧?我看你简历写了“熟悉Java多线程编程”,那你先说说,ThreadRunnable有什么区别?

谢飞机:这个我知道!Thread是类,Runnable是接口!而且 Java 不支持多继承,所以用Runnable更好!

面试官(微微点头):不错,理解到位。那你说说,为什么推荐使用线程池而不是手动创建线程?

谢飞机:因为……new Thread() 太费内存了!就像买手机,不能每次打电话都买个新手机吧?得租!线程池就是租赁公司!

面试官(嘴角微扬):比喻有点意思。那你知道ExecutorService怎么关闭吗?

谢飞机:当然!调用shutdown()就行!它会等任务执行完再关。还有个shutdownNow(),那是直接拔电源!

面试官:还行。那如果我想让主线程等待所有子线程完成后再继续,怎么做?

谢飞机:用join()啊!或者CountDownLatch,我还会用CyclicBarrier呢!

面试官:嗯,基本功可以。


第二轮提问:JUC 并发工具进阶

面试官:既然提到CountDownLatch,说说它和CyclicBarrier的区别?

谢飞机:呃……都是倒数计数器?一个是从10减到0,一个是反过来加?

面试官:……

谢飞机:哦不对!CountDownLatch是一个线程等其他多个线程完成;CyclicBarrier是多个线程互相等,大家一起出发,像百米赛跑!

面试官:勉强及格。那Semaphore呢?用来做什么?

谢飞机:信号灯!控制并发数量的!比如停车场有10个车位,Semaphore(10),进来一辆车 acquire(),出去 release()!

面试官:例子不错。那ReentrantLocksynchronized有啥区别?

谢飞机:都能加锁!但ReentrantLock更高级,可以尝试锁、可中断、还能指定公平锁!

面试官:那它是怎么实现可重入的?

谢飞机:呃……内部有个计数器?谁持有锁就记名字?具体我忘了,反正能重入!

面试官(皱眉):行吧……


第三轮提问:线程安全与实战场景

面试官:现在有一个高并发场景,多个线程同时对一个共享变量进行累加操作,你会怎么处理?

谢飞机:用volatile

面试官volatile能保证原子性吗?

谢飞机:呃……能?不能?好像不能……那用synchronized块包起来!

面试官:还有别的办法吗?

谢飞机:用AtomicInteger!CAS机制!无锁并发!

面试官:那 CAS 有什么缺点?

谢飞机:ABA问题!还有自旋太耗CPU!

面试官:怎么解决 ABA?

谢飞机:呃……加版本号!AtomicStampedReference

面试官:不错。最后一个问题:线程池的核心参数有哪些?

谢飞机:corePoolSize、maximumPoolSize、workQueue、keepAliveTime、threadFactory、handler……

面试官:如果队列满了且线程数达到最大,会发生什么?

谢飞机:看拒绝策略!默认是 AbortPolicy,抛异常!还可以自己定义!

面试官:好,今天的面试就到这里。你的基础还行,有些地方需要加强。回去等通知吧。

谢飞机(起身鞠躬):谢谢面试官!我回去就把 JUC 源码打印出来当被子盖!


参考答案详解

1. Thread 和 Runnable 的区别

  • Thread是类,Runnable是接口。
  • Java 单继承限制下,实现Runnable更灵活。
  • 实际上Thread类也实现了Runnable接口。
  • 推荐使用RunnableCallable配合线程池使用。

2. 为什么使用线程池?

  • 避免频繁创建/销毁线程带来的资源消耗。
  • 控制并发数量,防止资源耗尽。
  • 提供统一的管理机制(监控、统计、拒绝策略等)。

3. ExecutorService 关闭方式

  • shutdown():温和关闭,不再接收新任务,等待已提交任务执行完毕。
  • shutdownNow():立即关闭,尝试中断正在运行的线程,返回未执行的任务列表。

4. 主线程等待子线程

  • thread.join():适用于少量线程。
  • CountDownLatch:适合一个或多个线程等待其他多个线程完成。
  • CyclicBarrier:多个线程相互等待,达到屏障点后一起继续。
  • Phaser:更灵活的同步工具,支持动态注册。

5. CountDownLatch vs CyclicBarrier

| 对比项 | CountDownLatch | CyclicBarrier | |--------|----------------|---------------| | 计数方向 | 向下减少 | 达到数量即触发 | | 是否可重用 | 不可重用 | 可重用(reset) | | 使用场景 | 主线程等待多个任务完成 | 多个线程互相等待 | | 实现机制 | 计数为0唤醒 | 达到阈值触发 |

6. Semaphore 作用

  • 用于控制同时访问特定资源的线程数量。
  • 常用于限流、资源池(如数据库连接池)。
  • acquire()获取许可,release()释放许可。

7. ReentrantLock vs synchronized

| 特性 | synchronized | ReentrantLock | |------|--------------|---------------| | 语法级别 | JVM 层面(关键字) | API 层面(代码调用) | | 可中断 | 否 | 是(lockInterruptibly) | | 超时获取 | 否 | 是(tryLock(timeout)) | | 公平性 | 非公平 | 可设置公平锁 | | 条件变量 | wait/notify | Condition |

8. ReentrantLock 实现可重入原理

  • 内部使用AQS(AbstractQueuedSynchronizer)实现。
  • 维护一个 state 变量表示锁状态,thread 记录持有锁的线程。
  • 同一线程重复获取锁时,state++,释放时 state--,直到为0才真正释放。

9. 共享变量并发累加解决方案

  • synchronized同步块
  • ReentrantLock显式锁
  • AtomicInteger等原子类(基于 CAS + volatile)
  • LongAdder(高并发下性能更好)

10. CAS 缺点

  • ABA问题:值从A→B→A,CAS 无法察觉中间变化。解决方案:AtomicStampedReference加版本号。
  • 自旋开销:循环重试可能导致 CPU 空转。可通过Thread.yield()优化。
  • 只能保证单个变量的原子性:多变量需使用锁或其他机制。

11. 线程池核心参数

  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:非核心线程空闲存活时间
  • unit:时间单位
  • workQueue:任务队列
  • threadFactory:线程工厂
  • handler:拒绝策略

12. 拒绝策略

  • AbortPolicy:抛出RejectedExecutionException
  • CallerRunsPolicy:由提交任务的线程执行
  • DiscardPolicy:静默丢弃
  • DiscardOldestPolicy:丢弃队列中最老的任务,重试提交

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询