Java多线程详解(一)

一、前言

本文将深入讲解Java多线程的核心内容,包括线程状态、多线程并发测试方法以及线程安全问题演示。通过大量的代码示例和运行截图,帮助大家彻底掌握Java多线程编程的基础。


二、线程生命周期详解

2.1 线程的六种状态

Java线程共有6种状态,定义在java.lang.Thread.State枚举中:

状态 说明
NEW 新建状态,线程被创建但未启动
RUNNABLE 可运行状态,等待CPU调度
BLOCKED 阻塞状态,等待获取监视器锁
WAITING 无限等待状态,需要被唤醒
TIMED_WAITING 限时等待状态,超时后自动唤醒
TERMINATED 终止状态,线程执行完毕

2.2 线程状态转换图

2.3 状态转换代码测试

java 复制代码
/**
 * 线程状态测试类
 * 演示Java线程的六种状态及其转换
 */
public class ThreadStateTest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("========== 线程状态测试开始 ==========
");

        // 1. 测试NEW状态
        Thread newThread = new Thread(() -> {});
        System.out.println("【NEW状态】新建线程但未启动: " + newThread.getState());

        // 2. 测试RUNNABLE状态
        Thread runnableThread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                // 保持运行状态
            }
        });
        runnableThread.start();
        Thread.sleep(100);
        System.out.println("【RUNNABLE状态】线程已启动正在运行: " + runnableThread.getState());
        runnableThread.interrupt();

        // 3. 测试BLOCKED状态
        Object lock = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                try { Thread.sleep(5000); } catch (InterruptedException e) {}
            }
        });
        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("t2获取到锁");
            }
        });
        t1.start();
        Thread.sleep(100); // 确保t1先获取锁
        t2.start();
        Thread.sleep(100);
        System.out.println("【BLOCKED状态】t2等待获取锁: " + t2.getState());

        // 4. 测试WAITING状态
        Object waitObj = new Object();
        Thread waitingThread = new Thread(() -> {
            synchronized (waitObj) {
                try { 
                    waitObj.wait(); // 无限等待
                } catch (InterruptedException e) {}
            }
        });
        waitingThread.start();
        Thread.sleep(100);
        System.out.println("【WAITING状态】线程调用wait()等待: " + waitingThread.getState());
        synchronized (waitObj) {
            waitObj.notify(); // 唤醒线程
        }

        // 5. 测试TIMED_WAITING状态
        Thread timedThread = new Thread(() -> {
            try { 
                Thread.sleep(10000); // 限时等待10秒
            } catch (InterruptedException e) {}
        });
        timedThread.start();
        Thread.sleep(100);
        System.out.println("【TIMED_WAITING状态】线程调用sleep(10000): " + timedThread.getState());
        timedThread.interrupt();

        // 6. 测试TERMINATED状态
        Thread terminatedThread = new Thread(() -> {
            System.out.println("线程执行中...");
        });
        terminatedThread.start();
        terminatedThread.join(); // 等待线程执行完毕
        System.out.println("【TERMINATED状态】线程执行完毕: " + terminatedThread.getState());

        System.out.println("
========== 线程状态测试结束 ==========");
    }
}

运行结果:

复制代码
========== 线程状态测试开始 ==========

【NEW状态】新建线程但未启动: NEW
【RUNNABLE状态】线程已启动正在运行: RUNNABLE
【BLOCKED状态】t2等待获取锁: BLOCKED
【WAITING状态】线程调用wait()等待: WAITING
【TIMED_WAITING状态】线程调用sleep(10000): TIMED_WAITING
线程执行中...
【TERMINATED状态】线程执行完毕: TERMINATED

========== 线程状态测试结束 ==========

2.4 状态转换详解

NEW -> RUNNABLE :调用start()方法后,线程进入可运行状态,等待CPU调度。

RUNNABLE -> BLOCKED :线程尝试获取synchronized锁失败时,进入阻塞状态。

RUNNABLE -> WAITING :线程调用Object.wait()Thread.join()LockSupport.park()时进入无限等待状态。

RUNNABLE -> TIMED_WAITING :线程调用sleep(long)wait(long)join(long)时进入限时等待状态。

BLOCKED/WAITING/TIMED_WAITING -> RUNNABLE:获取锁成功、被唤醒或超时后,线程重新进入可运行状态。

RUNNABLE -> TERMINATED :线程的run()方法执行完毕或抛出未捕获异常时,进入终止状态。


三、多线程并发测试

3.1 基础多线程并发执行测试

java 复制代码
/**
 * 基础多线程并发测试
 * 演示多个线程同时执行的效果
 */
public class BasicMultiThreadTest {
    public static void main(String[] args) {
        System.out.println("主线程开始执行...
");

        // 创建3个线程并发执行
        for (int i = 0; i < 3; i++) {
            final int threadNum = i;
            Thread thread = new Thread(() -> {
                System.out.println("【" + Thread.currentThread().getName() + "】开始执行");
                for (int j = 0; j < 5; j++) {
                    System.out.println("【" + Thread.currentThread().getName() + "】执行中... 计数: " + j);
                    try {
                        Thread.sleep(100); // 模拟任务执行
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("【" + Thread.currentThread().getName() + "】执行完毕
");
            }, "Worker-" + i);
            thread.start();
        }

        System.out.println("主线程继续执行其他任务...");

        // 等待所有子线程执行完毕
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程结束");
    }
}

结果分析:

  • 三个线程的执行顺序是不确定的,由CPU调度决定
  • 主线程在启动子线程后继续执行,不会等待子线程
  • 每个线程独立执行自己的任务,互不干扰

3.2 线程执行顺序测试

java 复制代码
/**
 * 线程执行顺序测试
 * 演示线程启动顺序与执行顺序的关系
 */
public class ThreadOrderTest {
    public static void main(String[] args) {
        System.out.println("========== 线程执行顺序测试 ==========
");

        // 创建5个线程
        Thread[] threads = new Thread[5];
        for (int i = 0; i < 5; i++) {
            final int num = i;
            threads[i] = new Thread(() -> {
                System.out.println("线程 " + num + " 执行,名称: " + Thread.currentThread().getName());
            }, "Thread-" + num);
        }

        // 按顺序启动线程
        System.out.println("按顺序启动线程...");
        for (Thread t : threads) {
            t.start();
        }

        System.out.println("
注意:启动顺序 ≠ 执行顺序!");
    }
}

运行结果:

复制代码
========== 线程执行顺序测试 ==========

按顺序启动线程...
线程 1 执行,名称: Thread-1
线程 0 执行,名称: Thread-0
线程 3 执行,名称: Thread-3
线程 2 执行,名称: Thread-2
线程 4 执行,名称: Thread-4

注意:启动顺序 ≠ 执行顺序!

3.3 线程优先级测试

java 复制代码
/**
 * 线程优先级测试
 * 演示线程优先级对执行的影响
 */
public class ThreadPriorityTest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("========== 线程优先级测试 ==========
");

        // 创建高优先级线程
        Thread highPriority = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("【高优先级】线程执行: " + i);
            }
        }, "High-Priority");
        highPriority.setPriority(Thread.MAX_PRIORITY); // 优先级10

        // 创建低优先级线程
        Thread lowPriority = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("【低优先级】线程执行: " + i);
            }
        }, "Low-Priority");
        lowPriority.setPriority(Thread.MIN_PRIORITY); // 优先级1

        // 创建普通优先级线程
        Thread normalPriority = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("【普通优先级】线程执行: " + i);
            }
        }, "Normal-Priority");
        normalPriority.setPriority(Thread.NORM_PRIORITY); // 优先级5

        // 同时启动
        lowPriority.start();
        normalPriority.start();
        highPriority.start();

        // 等待所有线程执行完毕
        lowPriority.join();
        normalPriority.join();
        highPriority.join();

        System.out.println("
注意:优先级只是建议,不保证执行顺序!");
    }
}

四、线程安全问题演示与测试

4.1 线程安全问题演示

当多个线程同时访问共享数据时,如果没有同步机制,就会出现数据不一致的问题。

java 复制代码
/**
 * 线程安全问题演示
 * 多个线程同时操作共享变量,导致数据不一致
 */
public class ThreadSafetyProblem {
    // 共享计数器
    private static int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        System.out.println("========== 线程安全问题演示 ==========
");
        System.out.println("初始值: counter = 0");
        System.out.println("期望结果: counter = 10000
");

        // 创建100个线程,每个线程对counter自增100次
        Thread[] threads = new Thread[100];
        for (int i = 0; i < 100; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    counter++; // 非原子操作,存在线程安全问题
                }
            }, "Thread-" + i);
            threads[i].start();
        }

        // 等待所有线程执行完毕
        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println("最终计数器值: " + counter);
        System.out.println("期望值: 10000");
        System.out.println("是否一致: " + (counter == 10000));

        if (counter != 10000) {
            System.out.println("
❌ 出现线程安全问题!丢失: " + (10000 - counter) + " 次自增");
        }
    }
}

运行结果(多次执行结果不同):

复制代码
========== 线程安全问题演示 ==========

初始值: counter = 0
期望结果: counter = 10000

最终计数器值: 9823
期望值: 10000
是否一致: false

❌ 出现线程安全问题!丢失: 177 次自增

再次运行:

复制代码
最终计数器值: 9567
期望值: 10000
是否一致: false

❌ 出现线程安全问题!丢失: 433 次自增

4.2 问题分析

counter++操作实际上分为三步:

  1. 读取:从内存读取counter的值到寄存器
  2. 修改:寄存器中的值加1
  3. 写入:将新值写回内存

当多个线程同时执行时,可能出现以下情况:

复制代码
时间线:
线程A: 读取counter=0  →  寄存器+1=1  →  [被抢占]  →  [恢复]  →  写入counter=1
线程B: 读取counter=0  →  寄存器+1=1  →  写入counter=1

结果: counter=1 (期望是2)

这就是典型的**竞态条件(Race Condition)**问题。

4.3 使用synchronized修复

java 复制代码
/**
 * 使用synchronized修复线程安全问题
 */
public class SynchronizedFixTest {
    private static int counter = 0;
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        System.out.println("========== synchronized修复测试 ==========
");

        Thread[] threads = new Thread[100];
        for (int i = 0; i < 100; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    synchronized (lock) { // 加锁保证原子性
                        counter++;
                    }
                }
            }, "Thread-" + i);
            threads[i].start();
        }

        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println("最终计数器值: " + counter);
        System.out.println("期望值: 10000");
        System.out.println("是否一致: " + (counter == 10000));

        if (counter == 10000) {
            System.out.println("
✅ 线程安全问题已修复!");
        }
    }
}

运行结果:

复制代码
========== synchronized修复测试 ==========

最终计数器值: 10000
期望值: 10000
是否一致: true

✅ 线程安全问题已修复!

4.4 使用AtomicInteger修复

java 复制代码
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 使用AtomicInteger修复线程安全问题
 */
public class AtomicIntegerFixTest {
    private static AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        System.out.println("========== AtomicInteger修复测试 ==========
");

        Thread[] threads = new Thread[100];
        for (int i = 0; i < 100; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    counter.incrementAndGet(); // CAS原子操作
                }
            }, "Thread-" + i);
            threads[i].start();
        }

        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println("最终计数器值: " + counter.get());
        System.out.println("期望值: 10000");
        System.out.println("是否一致: " + (counter.get() == 10000));

        if (counter.get() == 10000) {
            System.out.println("
✅ 使用CAS原子操作修复成功!");
        }
    }
}

运行结果:

复制代码
========== AtomicInteger修复测试 ==========

最终计数器值: 10000
期望值: 10000
是否一致: true

✅ 使用CAS原子操作修复成功!

五、多线程性能测试

5.1 单线程 vs 多线程性能对比

java 复制代码
/**
 * 单线程与多线程性能对比测试
 */
public class PerformanceCompareTest {
    private static final int COUNT = 100000000; // 1亿次计算

    public static void main(String[] args) throws InterruptedException {
        System.out.println("========== 性能对比测试 ==========
");

        // 单线程测试
        long start1 = System.currentTimeMillis();
        long sum1 = 0;
        for (int i = 0; i < COUNT; i++) {
            sum1 += i;
        }
        long end1 = System.currentTimeMillis();
        System.out.println("单线程结果: " + sum1);
        System.out.println("单线程耗时: " + (end1 - start1) + "ms
");

        // 多线程测试(4个线程)
        long start2 = System.currentTimeMillis();
        Thread[] threads = new Thread[4];
        long[] sums = new long[4];

        for (int t = 0; t < 4; t++) {
            final int threadIndex = t;
            threads[t] = new Thread(() -> {
                long partialSum = 0;
                int start = threadIndex * (COUNT / 4);
                int end = (threadIndex + 1) * (COUNT / 4);
                for (int i = start; i < end; i++) {
                    partialSum += i;
                }
                sums[threadIndex] = partialSum;
            });
            threads[t].start();
        }

        for (Thread t : threads) {
            t.join();
        }

        long sum2 = sums[0] + sums[1] + sums[2] + sums[3];
        long end2 = System.currentTimeMillis();
        System.out.println("多线程结果: " + sum2);
        System.out.println("多线程耗时: " + (end2 - start2) + "ms
");

        System.out.println("性能提升: " + (end1 - start1) / (double)(end2 - start2) + "x");
    }
}

运行结果(4核CPU):

复制代码
========== 性能对比测试 ==========

单线程结果: 4999999950000000
单线程耗时: 320ms

多线程结果: 4999999950000000
多线程耗时: 95ms

性能提升: 3.37x

5.2 线程创建开销测试

java 复制代码
/**
 * 线程创建开销测试
 * 对比直接创建线程和使用线程池的性能差异
 */
public class ThreadCreationOverheadTest {
    private static final int TASK_COUNT = 1000;

    public static void main(String[] args) throws InterruptedException {
        System.out.println("========== 线程创建开销测试 ==========
");

        // 方法1:直接创建线程
        long start1 = System.currentTimeMillis();
        Thread[] threads = new Thread[TASK_COUNT];
        for (int i = 0; i < TASK_COUNT; i++) {
            threads[i] = new Thread(() -> {
                // 简单任务
                int sum = 0;
                for (int j = 0; j < 100; j++) {
                    sum += j;
                }
            });
            threads[i].start();
        }
        for (Thread t : threads) {
            t.join();
        }
        long end1 = System.currentTimeMillis();
        System.out.println("直接创建" + TASK_COUNT + "个线程耗时: " + (end1 - start1) + "ms");

        // 方法2:使用线程池
        ExecutorService executor = Executors.newFixedThreadPool(10);
        long start2 = System.currentTimeMillis();
        for (int i = 0; i < TASK_COUNT; i++) {
            executor.execute(() -> {
                int sum = 0;
                for (int j = 0; j < 100; j++) {
                    sum += j;
                }
            });
        }
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);
        long end2 = System.currentTimeMillis();
        System.out.println("使用线程池(10线程)耗时: " + (end2 - start2) + "ms");

        System.out.println("
结论:线程池可以显著减少线程创建销毁的开销!");
    }
}

运行结果:

复制代码
========== 线程创建开销测试 ==========

直接创建1000个线程耗时: 1567ms
使用线程池(10线程)耗时: 234ms

结论:线程池可以显著减少线程创建销毁的开销!

六、线程调试技巧

6.1 IDEA多线程调试

在IntelliJ IDEA中调试多线程程序:

  1. 在断点处右键,选择"Suspend" -> "Thread"
  2. 在Debug窗口的"Threads"标签页查看所有线程状态
  3. 可以切换不同线程进行单步调试

6.2 使用jstack查看线程状态

bash 复制代码
# 查看Java进程的线程堆栈
jstack -l <pid>

# 示例输出:
"Worker-0" #11 prio=5 os_prio=0 tid=0x00007f8b0c0d1800 nid=0x3e03 waiting on condition [0x000070000b5a5000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at BasicMultiThreadTest.lambda$main$0(BasicMultiThreadTest.java:15)
        at BasicMultiThreadTest$$Lambda$1/791452441.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

6.3 使用jconsole监控线程

bash 复制代码
jconsole

可以可视化查看:

  • 线程数量变化
  • 线程状态分布
  • 死锁检测

七、总结

本文详细介绍了Java多线程的基础内容:

  1. 线程生命周期:6种状态及其转换条件
  2. 多线程并发测试:基础并发执行、执行顺序、优先级测试
  3. 线程安全问题:竞态条件演示、synchronized和AtomicInteger修复方案
  4. 性能测试:单线程vs多线程、线程创建开销对比
  5. 调试技巧:IDEA调试、jstack、jconsole工具使用

后续文章将继续深入讲解线程池、JUC并发工具类、CompletableFuture异步编程等高级内容,敬请期待!


如果觉得本文对你有帮助,欢迎点赞、收藏、评论!

关注博主,持续更新Java技术干货!

相关推荐
Scott9999HH10 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔10 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社11 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海11 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖12 小时前
JDK 26 新特性详解
java·开发语言
马优晨12 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区14 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大15 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai15 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说15 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json