最近正在复习Java八股,所以会将一些热门的八股问题,结合ai与自身理解写成博客便于记忆
今天将以这四个问题为依据。
一、JMM内存模型解析
1. JMM核心概念
Java内存模型(Java Memory Model)定义了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量的底层细节。
三大核心问题:
- 原子性:一个操作或多个操作要么全部执行成功,要么全部不执行
- 可见性:当一个线程修改了共享变量的值,其他线程能够立即看到修改后的值
- 有序性:程序执行的顺序按照代码的先后顺序执行(允许指令重排序优化)
2. JMM内存结构
[线程工作内存] ←→ [主内存](私有) (共享)
- 工作内存:每个线程私有的内存空间
- 主内存:所有线程共享的内存区域
二、JMM如何保证三大特性
1. 原子性保障
实现机制:
- 基本类型访问(除long/double)具有原子性
- synchronized块(monitorenter/monitorexit)
- Lock接口的实现类
- Atomic原子类(CAS操作)
示例:
private AtomicInteger count = new AtomicInteger(0);public void increment() {count.incrementAndGet(); // CAS原子操作
}
2. 可见性保障
实现机制:
- volatile关键字(禁止缓存,直接读写主内存)
- synchronized(解锁前写回主内存,加锁时清空工作内存)
- final(正确初始化后不可变)
volatile示例:
private volatile boolean flag = false;public void setFlag() {flag = true; // 修改立即对其他线程可见
}
3. 有序性保障
实现机制:
- volatile(禁止指令重排序)
- synchronized(同一时刻只有一个线程执行)
- happens-before原则(编译器/处理器必须遵守的规则)
happens-before规则:
- 程序顺序规则
- 锁规则
- volatile规则
- 传递性规则
- 线程启动/终止规则
三、多线程交替打印0-100
1. synchronized实现
public class AlternatePrint {private static int num = 0;private static final Object lock = new Object();public static void main(String[] args) {new Thread(() -> {while (num <= 100) {synchronized (lock) {if (num % 2 == 0) {System.out.println(Thread.currentThread().getName() + ": " + num++);lock.notify();} else {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}, "EvenThread").start();new Thread(() -> {while (num <= 100) {synchronized (lock) {if (num % 2 != 0) {System.out.println(Thread.currentThread().getName() + ": " + num++);lock.notify();} else {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}, "OddThread").start();}
}
2. ReentrantLock实现
public class AlternatePrintWithLock {private static int num = 0;private static final ReentrantLock lock = new ReentrantLock();private static final Condition condition = lock.newCondition();public static void main(String[] args) {Thread evenThread = new Thread(() -> {while (num <= 100) {lock.lock();try {if (num % 2 == 0) {System.out.println(Thread.currentThread().getName() + ": " + num++);condition.signal();} else {condition.await();}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}, "EvenThread");Thread oddThread = new Thread(() -> {while (num <= 100) {lock.lock();try {if (num % 2 != 0) {System.out.println(Thread.currentThread().getName() + ": " + num++);condition.signal();} else {condition.await();}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}, "OddThread");evenThread.start();oddThread.start();}
}
四、多线程交替打印ABC
1. 精准控制版本(三个线程)
public class AlternatePrintABC {private static final ReentrantLock lock = new ReentrantLock();private static final Condition conditionA = lock.newCondition();private static final Condition conditionB = lock.newCondition();private static final Condition conditionC = lock.newCondition();private static volatile int state = 0;public static void main(String[] args) {new Thread(() -> {for (int i = 0; i < 10; ) {lock.lock();try {while (state % 3 != 0) {conditionA.await();}System.out.print("A");state++;i++;conditionB.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}, "Thread-A").start();new Thread(() -> {for (int i = 0; i < 10; ) {lock.lock();try {while (state % 3 != 1) {conditionB.await();}System.out.print("B");state++;i++;conditionC.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}, "Thread-B").start();new Thread(() -> {for (int i = 0; i < 10; ) {lock.lock();try {while (state % 3 != 2) {conditionC.await();}System.out.print("C");state++;i++;conditionA.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}, "Thread-C").start();}
}
2. 简化版本(使用单个Condition)
public class AlternatePrintABCSimple {private static final Object lock = new Object();private static volatile int state = 0;public static void main(String[] args) {new Thread(() -> {synchronized (lock) {for (int i = 0; i < 10; i++) {while (state % 3 != 0) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print("A");state++;lock.notifyAll();}}}, "Thread-A").start();new Thread(() -> {synchronized (lock) {for (int i = 0; i < 10; i++) {while (state % 3 != 1) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print("B");state++;lock.notifyAll();}}}, "Thread-B").start();new Thread(() -> {synchronized (lock) {for (int i = 0; i < 10; i++) {while (state % 3 != 2) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print("C");state++;lock.notifyAll();}}}, "Thread-C").start();}
}
五、关键知识点总结
1. JMM要点
- 原子性:关注操作是否被中断
- 可见性:关注修改是否及时可见
- 有序性:关注执行顺序是否符合预期
2. 线程通信要点
- wait/notify:必须在同步块中使用
- Condition:可创建多个等待队列
- volatile:适合单一状态标志
3. 交替打印模式
- 状态变量:控制打印顺序
- 精确唤醒:Condition优于notifyAll
- 循环检查:防止虚假唤醒
六、扩展思考
1. 如何实现N个线程交替打印?
解决方案:
- 使用state % N判断当前应打印的线程
- 每个线程对应一个Condition
- 链式唤醒(A→B→C→…→N→A)
2. 如何避免死锁?
检查清单:
- 避免嵌套锁
- 使用tryLock设置超时
- 统一获取锁的顺序
- 使用jstack分析死锁