首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【关于Java的多线程】

【关于Java的多线程】

作者头像
艾伦耶格尔
发布2025-08-28 15:46:46
发布2025-08-28 15:46:46
3450
举报
文章被收录于专栏:Java基础Java基础

你有没有遇到过这样的问题?

👉 想让程序同时做几件事,结果用了 new Thread(),代码乱成一锅粥? 👉 线程明明启动了,为什么数据不对?i++ 居然不准? 👉 面试官问:“说说 Java 多线程?” 你脑子里闪过 synchronizedvolatileThread,但串不起来,说不完整……


一、多线程是啥?—— 让 CPU “同时干多件事”

想象一下你在家做饭:

  • 单线程:你先洗菜 → 切菜 → 炒菜 → 盛盘 → 洗碗。 一件事做完,才能做下一件,效率低。
  • 多线程:你让老婆煮饭,孩子摆碗筷,自己炒菜。 三件事同时进行,饭更快吃上!

在 Java 世界里:

多线程 = 让程序“同时”执行多个任务 利用多核 CPU,提升程序吞吐量响应速度


二、线程 vs 进程:别再傻傻分不清

概念

进程(Process)

线程(Thread)

类比

一个“厨房”

厨房里的“一个厨师”

资源

独立内存空间、文件句柄

共享所属进程的资源

创建开销

大(系统调用)

小(JVM 内)

通信

复杂(管道、共享内存)

简单(直接读写共享变量)

安全性

隔离好,一个崩不影响其他

共享数据,一个出错可能影响整个进程

Java 中,一个进程可以有多个线程,它们共享堆内存,但栈是私有的。


三、创建线程的 4 种方式

方式1:继承 Thread 类(不推荐)
代码语言:javascript
复制
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("我是线程:" + Thread.currentThread().getName());
    }
}

// 使用
new MyThread().start(); // 必须调用 start(),不是 run()!

❌ 缺点:Java 不支持多继承,扩展性差。


方式2:实现 Runnable 接口(推荐)
代码语言:javascript
复制
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("我是线程:" + Thread.currentThread().getName());
    }
}

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

✅ 优点:解耦,实现类可以继承其他类。


方式3:实现 Callable + FutureTask(能返回结果)
代码语言:javascript
复制
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        return "任务完成!";
    }
}

// 使用
FutureTask<String> task = new FutureTask<>(new MyCallable());
new Thread(task).start();

String result = task.get(); // 阻塞等待结果
System.out.println(result);

✅ 优点:可以获取线程执行结果,还能抛异常。


方式4:线程池(生产环境唯一推荐方式!)
代码语言:javascript
复制
ExecutorService pool = Executors.newFixedThreadPool(5);

pool.submit(() -> {
    System.out.println("我在线程池中运行:" + Thread.currentThread().getName());
});

// 用完记得关闭!
pool.shutdown();

为什么必须用线程池?

  • 避免频繁创建/销毁线程,性能损耗大
  • 控制并发数,防止资源耗尽(OOM)
  • 提供任务队列、拒绝策略等高级功能

四、线程的 5 种状态

  • Newnew Thread(),还没调用 start()
  • Runnable:调用了 start(),等待 CPU 调度(就绪 + 运行)
  • Blocked:等待锁、sleep()wait() 等
  • Waiting / Timed Waitingwait()join()sleep() 等
  • Terminated:线程执行完 run() 或异常退出

五、线程安全:为什么 i++ 不安全?

问题代码:
代码语言:javascript
复制
int i = 0;
// 10 个线程,每个 ++ 1000 次
for (int j = 0; j < 1000; j++) {
    i++;
}

结果很可能 ≠ 10000!

为什么?

因为 i++ 不是原子操作!它分三步:

  1. 读取 i 的值
  2. +1
  3. 写回内存

两个线程可能同时读到 i=5,都+1,都写回 6,结果只+1,而不是+2。

线程安全 = 多线程访问共享数据时,结果正确


六、解决线程安全的 3 大法宝

1. synchronized:给代码“上锁”
代码语言:javascript
复制
// 方式1:修饰方法
public synchronized void add() {
    i++;
}

// 方式2:修饰代码块
synchronized (this) {
    i++;
}

✅ 原理:每个对象有一把“内置锁”(Monitor),拿到锁才能执行。


2. volatile:保证“可见性”
代码语言:javascript
复制
private volatile boolean running = true;

public void run() {
    while (running) {
        // 执行任务
    }
}

✅ 作用:

  • 保证变量对所有线程立即可见(修改后立刻写回主内存)
  • 禁止指令重排序 ❌ 不能保证原子性(i++ 还是不安全)

3. java.util.concurrent 包:并发工具全家桶
✅ AtomicInteger:原子类
代码语言:javascript
复制
private AtomicInteger i = new AtomicInteger(0);

i.incrementAndGet(); // 原子性 i++
✅ ReentrantLock:显式锁
代码语言:javascript
复制
private ReentrantLock lock = new ReentrantLock();

lock.lock();
try {
    i++;
} finally {
    lock.unlock();
}

✅ 比 synchronized 更灵活:可尝试锁、可中断、公平锁。

✅ 线程安全集合
  • ConcurrentHashMap:高并发 HashMap
  • CopyOnWriteArrayList:读多写少场景
  • BlockingQueue:生产者-消费者模型
代码语言:javascript
复制
BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
queue.put("item"); // 队列满时阻塞
String item = queue.take(); // 队列空时阻塞

七、线程间通信:wait/notify、Condition、BlockingQueue

场景:生产者-消费者模型
代码语言:javascript
复制
// 用 BlockingQueue 最简单!
BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);

// 生产者
new Thread(() -> {
    for (int i = 1; i <= 10; i++) {
        try {
            queue.put("产品" + i);
            System.out.println("生产:" + "产品" + i);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}).start();

// 消费者
new Thread(() -> {
    while (true) {
        try {
            String product = queue.take();
            System.out.println("消费:" + product);
            if (product.equals("产品10")) break;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}).start();

BlockingQueue 内置了线程通信机制,是首选方案!


八、高频问题 & 高分回答

Q1: synchronized 和 ReentrantLock 有什么区别?

区别

synchronized

ReentrantLock

实现

JVM 内置锁(Monitor)

JDK 代码实现

释放

自动(方法结束或异常)

必须手动 unlock()

灵活性

高(可尝试、可中断、公平锁)

性能

JDK 1.6+ 已优化,差距不大

略高(竞争激烈时)

💬 高分回答: “synchronized 简单,自动释放,适合大多数场景;ReentrantLock 更灵活,比如我们做秒杀系统时,用 tryLock(timeout) 避免无限等待。”


Q2: volatile 能保证原子性吗?

不能! volatile 只保证可见性有序性,不保证原子性。 比如 i++ 操作,即使 ivolatile,也可能出错。 要保证原子性,要用 synchronizedAtomicInteger


Q3: 线程池的核心参数有哪些?
代码语言:javascript
复制
new ThreadPoolExecutor(
    5,          // corePoolSize:核心线程数
    10,         // maximumPoolSize:最大线程数
    60L,        // keepAliveTime:空闲线程存活时间
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(100), // workQueue:任务队列
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

拒绝策略

  • AbortPolicy:抛异常(默认)
  • CallerRunsPolicy:由调用者线程执行
  • DiscardPolicy:静默丢弃
  • DiscardOldestPolicy:丢弃队列最老任务

Q4: 什么是线程上下文切换?如何减少?

: CPU 从一个线程切换到另一个线程执行的过程。 它涉及保存/恢复寄存器、缓存失效,开销大。 减少方法:

  1. 减少线程数(用线程池)
  2. 避免频繁阻塞(如减少锁竞争)
  3. 用 volatile 或 CAS 减少锁使用

✅ 总结:一张表搞懂多线程核心

问题

答案

创建线程

用线程池(ExecutorService)

线程安全

synchronized、volatile、Atomic、ReentrantLock

通信

BlockingQueue > wait/notify > Condition

集合

ConcurrentHashMap、CopyOnWriteArrayList

怎么说

“我用线程池管理任务,ConcurrentHashMap 存缓存,AtomicInteger 做计数”


🔚 最后一句话

多线程不是“炫技”,而是“高并发系统的发动机”。 从 Tomcat 处理请求,到 Redis 处理命令,再到 Kafka 消费消息, 背后都是多线程在高效协作。 掌握它,你才能写出高性能、线程安全的程序!

希望这篇能帮你彻底搞懂 Java 多线程!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、多线程是啥?—— 让 CPU “同时干多件事”
  • 二、线程 vs 进程:别再傻傻分不清
  • 三、创建线程的 4 种方式
    • 方式1:继承 Thread 类(不推荐)
    • 方式2:实现 Runnable 接口(推荐)
    • 方式3:实现 Callable + FutureTask(能返回结果)
    • 方式4:线程池(生产环境唯一推荐方式!)
  • 四、线程的 5 种状态
  • 五、线程安全:为什么 i++ 不安全?
    • 问题代码:
    • 为什么?
  • 六、解决线程安全的 3 大法宝
    • 1. synchronized:给代码“上锁”
    • 2. volatile:保证“可见性”
    • 3. java.util.concurrent 包:并发工具全家桶
      • ✅ AtomicInteger:原子类
      • ✅ ReentrantLock:显式锁
      • ✅ 线程安全集合
  • 七、线程间通信:wait/notify、Condition、BlockingQueue
    • 场景:生产者-消费者模型
  • 八、高频问题 & 高分回答
    • Q1: synchronized 和 ReentrantLock 有什么区别?
    • Q2: volatile 能保证原子性吗?
    • Q3: 线程池的核心参数有哪些?
    • Q4: 什么是线程上下文切换?如何减少?
  • ✅ 总结:一张表搞懂多线程核心
  • 🔚 最后一句话
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档