Skip to content

🔄 多线程基础

多线程概述

多线程(Multithreading)允许程序同时执行多个任务,提高程序的并发性和响应性。

线程与进程

  • 进程:程序的一次执行,有独立的地址空间
  • 线程:进程内的执行单元,共享进程的地址空间

创建线程

方式1:继承 Thread 类

java
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("线程运行: " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 使用
MyThread thread = new MyThread();
thread.start();

方式2:实现 Runnable 接口

java
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("线程运行: " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 使用
Thread thread = new Thread(new MyRunnable());
thread.start();

方式3:Lambda 表达式(Java 8+)

java
Thread thread = new Thread(() -> {
    for (int i = 0; i < 5; i++) {
        System.out.println("线程运行: " + i);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
thread.start();

线程状态

线程状态转换

新建 → 就绪 → 运行 → 阻塞 → 就绪 → 运行 → 终止

线程状态方法

java
Thread thread = new Thread(() -> {
    // 线程代码
});

// 获取状态
Thread.State state = thread.getState();
System.out.println("线程状态: " + state);

// 启动线程
thread.start();

// 等待线程完成
thread.join();

// 中断线程
thread.interrupt();

// 检查是否中断
boolean interrupted = thread.isInterrupted();

线程同步

synchronized 关键字

java
public class Counter {
    private int count = 0;
    
    // 同步方法
    public synchronized void increment() {
        count++;
    }
    
    // 同步代码块
    public void increment2() {
        synchronized (this) {
            count++;
        }
    }
}

wait 和 notify

java
public class ProducerConsumer {
    private int value;
    private boolean hasValue = false;
    
    public synchronized void produce(int val) {
        while (hasValue) {
            try {
                wait();  // 等待消费者消费
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        value = val;
        hasValue = true;
        notify();  // 通知消费者
    }
    
    public synchronized int consume() {
        while (!hasValue) {
            try {
                wait();  // 等待生产者生产
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        hasValue = false;
        notify();  // 通知生产者
        return value;
    }
}

可见性、有序性与原子性(JMM 速览)

  • 可见性:一个线程对共享变量的写入何时对其他线程可见(volatile/锁)。
  • 有序性:编译器与 CPU 允许重排序,但需满足 happens-before 规则。
  • 原子性:不可再分的操作,long/double 在早期 32 位上非原子(现代实现已保证)。

volatile 的正确用法

java
class StopSignal {
    private volatile boolean stopped = false;
    public void stop() { stopped = true; }
    public void run() {
        while (!stopped) {
            // do work
        }
    }
}
  • 保障可见性与禁止特定场景下的重排序;不保证复合操作的原子性。

指令重排与 DCL 单例

java
public class Singleton {
    private static volatile Singleton INSTANCE;
    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) INSTANCE = new Singleton();
            }
        }
        return INSTANCE;
    }
}

锁与并发原语

synchronized 的对象锁与类锁

  • 实例方法锁的是 this,静态方法锁的是 Class 对象。
  • 尽量缩小同步代码块,避免无谓竞争。

ReentrantLock 与条件队列

java
Lock lock = new ReentrantLock();
Condition notEmpty = lock.newCondition();
Condition notFull = lock.newCondition();
  • 支持可中断、可定时、公平/非公平、多个条件队列。

读写锁与 StampedLock(简介)

  • ReentrantReadWriteLock:读多写少的场景有效。
  • StampedLock:支持乐观读,需注意中断与可重入限制。

线程间协作模式

生产者-消费者(BlockingQueue)

java
BlockingQueue<Integer> q = new ArrayBlockingQueue<>(100);
// 生产者:q.put(x); 消费者:q.take();

Future/CompletableFuture 概览

java
CompletableFuture<Integer> f = CompletableFuture.supplyAsync(() -> 42)
    .thenApply(x -> x + 1)
    .exceptionally(ex -> -1);

线程池实践

为什么优先使用线程池

  • 复用线程降低创建销毁成本;可控的并发度;可观测与监控。

正确创建线程池(避免 Executors 工厂默认陷阱)

java
ThreadPoolExecutor pool = new ThreadPoolExecutor(
    8, 16,
    60, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(1000),
    new ThreadFactoryBuilder().setNameFormat("biz-pool-%d").build(),
    new ThreadPoolExecutor.CallerRunsPolicy()
);
  • 自定义队列与拒绝策略;命名线程便于定位;关注饱和时行为。

线程池参数调优指引

  • 计算密集:核心线程≈CPU 核心数或略多;IO 密集:核心线程数×(1+平均阻塞系数)。
  • 使用监控(JFR/Arthas/日志)观察队列长度、活跃线程与任务耗时。

常见并发 Bug 与定位

  • 死锁:循环等待 + 互斥 + 不可剥夺 + 占有且等待。
java
// 使用 jstack <pid> 查看 FOUND ONE JAVA-LEVEL DEADLOCK
  • 活锁:线程不断重试互相谦让但无进展。
  • 饥饿:低优先级线程长期得不到调度(不建议修改线程优先级)。

ThreadLocal 与内存泄漏

  • 在线程池复用下,ThreadLocal 值需及时清理:try { set } finally { remove }
  • 避免在大对象/连接上长期持有引用。

False Sharing(伪共享)

  • 多线程写入相邻内存导致同一缓存行反复失效。
  • 通过 @Contended(需 -XX:-RestrictContended)或手动填充避免。

练习与思考

  1. BlockingQueue 实现多生产者-多消费者,统计吞吐与延迟。
  2. synchronized 改为 ReentrantLock,在超时与可中断场景比较表现。
  3. 使用 CompletableFuture 改造串行调用为并行聚合。

完整示例

java
public class ThreadExample {
    public static void main(String[] args) {
        // 创建线程
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("线程1: " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("线程2: " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        
        // 启动线程
        thread1.start();
        thread2.start();
        
        // 等待线程完成
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("所有线程完成");
    }
}

下一步

掌握了多线程基础后,可以继续学习:


💡 提示:多线程编程可以提高程序性能,但需要注意线程安全问题