八股

synchronized 的锁升级过程

无锁 ↓(线程获得锁) 偏向锁(记录线程ID) ↓(出现其他线程竞争) 撤销偏向锁 ↓(CAS 成功) 轻量级锁(用户态自旋锁) ↓(CAS 多次失败) 重量级锁(内核态阻塞锁)

面试总结

  • 面试官:介绍一下new线程的几种方式
  • 我:

    1. 继承Thread 最简单的
    2. 实现 Runnable 接口,需要重写run方法
    3. 继承 Callable 接口 + FutureTask (有返回值)
  • 面试官: 三个线程,依次打印数字 1 ~ 9,每个线程打印一个数字,三个线程之间 轮流执行,形成交替打印的效果。简单说一下思路

  • 我:

    我会声明两个公共变量,一个记录线程id和应该打印的数字,并使用锁去锁住代码块,每一个线程过来的时候都需要拥有锁,并且判断线程id必须和公共变量的线程id一致才能打印,打印之后线程id会自增,并唤醒所有等待的线程,再次去竞争锁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    public class AlternatePrint {
    private static final Object lock = new Object();
    private static int number = 1; // 当前要打印的数字
    private static int threadFlag = 1; // 当前该哪个线程执行,取值为1/2/3

    public static void main(String[] args) {
    Runnable task1 = () -> printNumber(1);
    Runnable task2 = () -> printNumber(2);
    Runnable task3 = () -> printNumber(3);

    new Thread(task1, "线程1").start();
    new Thread(task2, "线程2").start();
    new Thread(task3, "线程3").start();
    }

    public static void printNumber(int threadId) {
    while (true) {
    synchronized (lock) {
    // 如果当前不是该线程打印,进入等待
    while (threadFlag != threadId) {
    try {
    lock.wait();
    } catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    }
    }

    // 如果数字超过9,结束所有线程
    if (number > 9) {
    lock.notifyAll();
    break;
    }

    System.out.println(Thread.currentThread().getName() + " 打印: " + number);
    number++;

    // 设置下一个线程标识:1 -> 2 -> 3 -> 1
    threadFlag = threadId % 3 + 1;

    // 唤醒所有线程
    lock.notifyAll();
    }
    }
    }
    }
  • 面试官:线程之间的通信方式有什么

  • 我:

    • ✅ 1. 共享变量通信(最常用)
      示例:使用 volatile 关键字保证可见性
    • ✅ 2. wait() / notify() / notifyAll()(配合 synchronized 使用)
      1. Lock + Condition(更灵活的等待/通知机制)
        • java.util.concurrent.locks.Condition 提供比 wait/notify 更灵活的方式,如多个条件队列。
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          Lock lock = new ReentrantLock();
          Condition condition = lock.newCondition();

          lock.lock();
          try {
          while (!conditionMet) {
          condition.await(); // 相当于 wait()
          }
          // do something
          condition.signal(); // 相当于 notify()
          } finally {
          lock.unlock();
          }
    • ✅ 4. join() 方法
      • 让一个线程等待另一个线程执行完后再继续。
        1
        2
        3
        4
        5
        6
        Thread t = new Thread(() -> {
        System.out.println("Child thread running");
        });
        t.start();
        t.join(); // 当前线程等 t 执行完
        System.out.println("Main thread resumes");
    • ✅ 5. 并发工具类(推荐使用)

      | 类名 | 功能说明 |
      | ———————— | —————————————————————————————————— |
      | BlockingQueue | 如 ArrayBlockingQueueLinkedBlockingQueue,线程安全队列,适用于生产者-消费者模型 |
      | CountDownLatch | 等待多个线程完成后再继续执行 |
      | CyclicBarrier | 等待多个线程到达某一屏障点后一起执行 |
      | Semaphore | 控制并发线程数量 |
      | Exchanger | 两个线程之间交换数据 |

  • 面试官:线程池的拒绝策略有哪些

  • 我:
    策略类行为说明
    AbortPolicy(默认)抛出 RejectedExecutionException 异常
    CallerRunsPolicy谁提交任务,谁就自己去执行(同步)
    DiscardPolicy直接丢弃任务,不抛异常
    DiscardOldestPolicy丢弃队列中最老的任务,尝试提交当前任务
  • 面试官:进程通信的有什么方式
  • 我:
    1. 使用数据库和redis

    2. Socket 通信 例如 netty

      • 适合多数 Java 多进程或多 JVM 程序之间通信。
    3. 读写文件
      • 一个写、一个读