JAVA多线程学习笔记(一)

260 阅读4分钟

一、多线程基础

1. 核心概念

1.1 进程 vs 线程
  • 进程:操作系统分配资源的基本单位。例如,运行一个Java程序就是一个进程,拥有独立的内存空间(堆、方法区等)。

  • 线程:进程内的执行单元。一个进程可以有多个线程,共享进程的内存资源(如堆内存),但每个线程有自己的程序计数器、栈、本地方法栈。

    比喻:进程像一家公司,线程是公司里的员工,共享办公室(内存资源),但各自处理不同任务。

1.2 并发 vs 并行
  • 并发(Concurrency) :单核CPU通过时间片轮转模拟“同时执行”,线程交替运行。

    // 示例:单核CPU交替执行线程A和线程B
    ThreadA.start();
    ThreadB.start(); 
    
  • 并行(Parallelism) :多核CPU真正同时执行多个线程。

    // 示例:四核CPU可同时运行4个线程
    
1.3 为什么需要多线程?
  • 提高吞吐量:例如,Web服务器用多线程同时处理多个请求。
  • 提升响应速度:例如,GUI程序用后台线程处理耗时任务,避免主线程卡死。
  • 充分利用多核CPU:现代CPU多为多核,多线程可发挥硬件性能。

2. 线程的创建方式

Java中创建线程的3种方式,各有适用场景:

2.1 继承Thread
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running: " + Thread.currentThread().getName());
    }
}

public static void main(String[] args) {
    MyThread thread = new MyThread();
    thread.start(); // 启动线程,JVM调用run()
}
  • 缺点:Java是单继承,若类已继承其他类,无法再继承Thread
2.2 实现Runnable接口(推荐)
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable running: " + Thread.currentThread().getName());
    }
}

public static void main(String[] args) {
    Thread thread = new Thread(new MyRunnable());
    thread.start();
}
  • 优点:避免单继承限制,任务与线程解耦,适合多线程共享同一任务。
2.3 实现Callable接口(带返回值)
class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return xxx; // 返回计算结果
    }
}

public static void main(String[] args) throws Exception {
    FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
    Thread thread = new Thread(futureTask);
    thread.start();
    int result = futureTask.get(); // 阻塞直到获取结果
    System.out.println("Result: " + result);
}
  • 适用场景:需要异步获取执行结果的场景(如计算密集型任务)。

3. 线程的生命周期

线程从创建到销毁经历多个状态,可通过Thread.getState()查看当前状态:

3.1 生命周期状态图
新建(New) → 就绪(Runnable) → 运行(Running) → 阻塞(Blocked) → 等待(Waiting) → 超时等待(Timed Waiting) → 终止(Terminated)
3.2 各状态详解
  • New:线程对象已创建,但未调用start()
  • Runnable:调用start()后进入就绪状态,等待CPU调度。
  • Running:线程获得CPU时间片,执行run()方法。
  • Blocked:线程等待获取锁(如进入synchronized代码块时锁被占用)。
  • Waiting:线程主动调用wait()join()LockSupport.park(),需其他线程唤醒。
  • Timed Waiting:调用sleep(ms)wait(timeout)等方法,超时后自动恢复。
  • Terminatedrun()执行完毕或发生未捕获异常。

4. 线程控制方法

4.1 核心方法
  • start() :启动线程,进入就绪状态(只能调用一次)。

  • run() :线程实际执行的代码,直接调用run()不会启动新线程!

  • join() :等待线程结束。

    Thread thread = new Thread(() -> {
        System.out.println("子线程执行");
    });
    thread.start();
    thread.join(); // 主线程等待子线程结束
    System.out.println("主线程继续");
    
  • sleep(long ms) :线程休眠指定毫秒,不释放锁

    try {
        Thread.sleep(1000); // 休眠1秒
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    
  • yield() :提示调度器让出当前CPU时间片,但调度器可能忽略。

4.2 中断机制
  • interrupt() :设置线程的中断标志(并非强制终止线程)。
  • isInterrupted() :检查中断标志是否被设置。
  • Thread.interrupted() :检查并清除中断标志。
正确的中断处理方式
Thread thread = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        // 正常执行任务
    }
    System.out.println("线程被中断,优雅退出");
});
thread.start();

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

5. 简单示例:多线程执行

public class BasicThreadExample {
    public static void main(String[] args) {
        // 创建两个线程
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread-1: " + i);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread-2: " + i);
            }
        });

        // 启动线程
        thread1.start();
        thread2.start();

        // 输出结果可能是交替的,取决于线程调度
    }
}

可能的输出

Thread-1: 0
Thread-2: 0
Thread-1: 1
Thread-2: 1
...

说明:线程执行顺序由操作系统调度器决定,结果可能每次不同。


关键点总结

  1. 线程是轻量级执行单元,共享进程资源,适合处理并发任务。
  2. 优先使用RunnableCallable,避免继承局限性。
  3. 理解线程状态转换,尤其是阻塞和等待的区别。
  4. 正确使用中断机制,避免用已废弃的stop()方法。