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技术干货!

相关推荐
会编程的土豆1 小时前
Go 语言中的 `new` 关键字(创建指针)
java·算法·golang
逸Y 仙X1 小时前
文章三十一:ElasticSearch 管道聚合
java·大数据·elasticsearch·搜索引擎·全文检索
Full Stack Developme1 小时前
Spring 发展历史
java·后端·spring
RSTJ_16252 小时前
PYTHON+AI LLM DAY FOURTY-EIGHT
开发语言·人工智能·python·深度学习
南宫萧幕2 小时前
HEV能量管理建模实战:从零搭建 Simulink 物理环境到 Python(DQN) 强化学习联合仿真调通
开发语言·python·算法·matlab·汽车·控制
lsx2024062 小时前
C++ 接口(抽象类)
开发语言
组合缺一2 小时前
Java 流程编排新范式 Solon Flow:一个引擎,七种节点,覆盖规则/任务/工作流/AI 编排全场景
java·spring·ai·solon·workflow·flow
handler012 小时前
【C++ 算法竞赛基础】数论篇:核心公式、经典例题与高频模板
开发语言·c++·算法·蓝桥杯·数论·最大公约数·最小公倍数
largecode2 小时前
企业号码认证可以线上办理吗?支持线上申请,设置来电显示品牌名
java·python·智能手机·微信公众平台·facebook·paddle·新浪微博