游乐游手机版
首页/AI教程/文章详情

多线程编程核心知识点全面总结

时间:2026-07-09 15:42
多线程核心:synchronized锁对象而非引用,volatile保证可见性不保证原子性;ReentrantLock支持tryLock和公平锁,Condition精细控制线程;ConcurrentHashMap等并发容器高效;线程池通过ExecutorService管理。

前言

最近在B站观看了马士兵老师关于Java多线程的精彩分享,我将核心知识点进行了系统梳理,整理成这份较为完整的多线程学习笔记。点击下图,即可一览本次讨论的全部内容概览。

多线程知识小结

正文

多线程基础

1. synchronized 锁定的代码块范围越小,效率自然越高——毕竟线程等待锁的时间会显著缩短。

2. synchronized 锁的是堆内存中的对象实例,而非对象的引用。若用于普通方法,锁的是 this,即当前对象在堆上的地址;若用于静态方法或静态变量,锁的是 Class 对象。一个常见的陷阱是:如果锁定的堆内存被重新赋值指向了新对象,原本使用同一引用的线程将不再受锁的约束。

3. volatile 保证变量在多线程间的可见性,但无法解决原子性问题,因此不能替代 synchronized。若需要原子性操作,可使用 AtomicInteger 等原子类,但其功能不如 synchronized 全面。

4. 切忌使用字符串常量作为锁对象。若两个字符串值相同(例如都是 "hello"),一个线程先锁住变量 a,另一个线程再去锁变量 b,极易引发死锁。这是因为 JVM 常量池会缓存字符串,导致两个引用指向同一对象。

5. wait() 会释放锁,而 notify() 不会——这两个方法是线程间通信的核心机制。使用它们时需要格外谨慎,否则容易导致线程逻辑混乱。

6. CountDownLatch 如同一个门闩:某个线程调用 await() 等待,直到其他线程将计数器减至 0。这与 TypeScript 中的 forkJoin 类似,专门用于等待多个线程完成状态变更后,主线程再继续执行。

7. 写操作加锁而读操作不加锁,大概率会出现脏读。常见的解决方案是 CopyOnWrite 机制——读操作不加锁,写操作时复制一份副本进行修改,保证读到的数据始终一致。

ReentrantLock 重入锁

1. ReentrantLock 可以替代 synchronized,但属于手工锁——必须手动释放,否则线程会一直阻塞。

2. 它和 synchronized 最大的不同在于支持 tryLock:尝试获取锁,获取成功则执行某任务,失败则执行另一任务,而非像 synchronized 那样盲目等待。

3. 通过构造函数的参数 true,可以创建“公平锁”。假设 5 个线程同时等待同一把锁释放,默认是非公平的——谁先抢到算谁的;而公平锁则会按等待时间排序,让等待最久的线程优先获得锁。

4. wait() 通常应与 while 循环配合使用,而非 if。因为 wait() 会释放锁,被唤醒时可能有多个线程同时被唤醒,若仅用 if 判断条件,会出现竞态问题。

5. 需要通知其他线程苏醒时,应使用 notifyAll()。若使用 ReentrantLock,还可借助 Condition 实现精准唤醒:通过 newCondition() 获取 Condition 对象,不同条件可创建不同的 Condition,从而实现更细粒度的线程唤醒控制。

6. ReentrantLock 与传统 Thread 方法的对应关系如下:

等待方法 通知方法 通知全部
condition.await() condition.signal() condition.signalAll()
wait() notify() notifyAll()

7. ThreadLocal 为每个线程提供独立的存储空间——这是典型的以空间换时间的策略。

并发容器

1. 若要确保 Listsize()remove() 操作具备原子性(例如购票系统),推荐使用 ConcurrentLinkedQueue。它的 poll() 方法先删除元素再返回,若返回 null 表示队列已空——这种“先删后验”的设计十分巧妙。

2. ConcurrentHashMap 取代了 HashTable:后者使用一个大锁锁住整个对象,而前者采用更细粒度的锁机制,每次只锁定一个 segment。更详细的设计可参考这篇文档 https://www.cnblogs.com/heyonggang/p/9112731.html

3. 在高并发场景下,若 Map 还需要排序功能,可用 ConcurrentSkipListMap 替代 TreeMap。它的插入效率相对较低,但查询速度很快——适合读多写少的场景。

4. CopyOnWriteList 采用写时复制策略:写入数据时复制一份新列表,在新列表上添加元素,最后将引用指向新列表。这种方式下,读操作完全无需加锁,因此读效率高、写效率低。

5. Collections.synchronizedXXX() 可以将未加锁的容器包装成线程安全版本,支持 ListSetMap 等类型。

6. 高并发环境下常用的两种队列:
- ConcurrentLinkedQueue
- BlockingQueueLinkedBlockingQueue 是无界队列,ArrayBlockingQueue 是有界队列,DelayQueue 用于定时任务)

队列的三个方法需注意区别:

  • add():队列满时直接抛出异常
  • offer():队列满时返回 false,不会报错
  • put():队列满时阻塞等待,直到有空间释放

7. DelayQueue 中的 take() 方法,若没有消费者取走元素,会一直阻塞等待。

8. SynchronousQueue 容量为 0,不能使用 add(),只能通过 put() 放入一个元素,然后阻塞等待消费者取走。

线程池

1. Executor 是线程池的顶级接口,仅包含一个 execute() 方法,接收 Runnable 参数。

2. ExecutorService 扩展了 Executor,可提交 RunnableCallable 任务,提供了 execute()submit() 两种方法。

3. CallableRunnable 的最大区别在于:call() 方法有返回值,且能够抛出异常。

4. Executors 是一个工具类,提供了五种初始化线程池的方式:

  • newFixedThreadPool:固定线程数量的线程池
  • newCachedThreadPool:缓存线程池,默认最大线程数为 Integer.MAX_VALUE,线程空闲 60 秒后自动销毁
  • newSingleThreadExecutor:单例线程池,保证任务按 FIFO 顺序执行
  • newScheduledThreadPool:支持定时任务的线程池
  • newWorkStealingPool:工作窃取线程池,每个线程拥有独立的任务队列,空闲线程会“窃取”其他线程的任务来执行

5. ThreadPool 的核心思想:保持固定数量的工作线程,空闲时不创建新线程,从而减少资源开销。

6. Future 作为 Callable 的返回值,可用于获取异步计算的结果。

7. 以下是演示代码——分别使用单线程和多线程计算指定范围内的所有质数,对比两者性能差异:

package msb_013;

import ja va.util.ArrayList;
import ja va.util.List;
import ja va.util.concurrent.Callable;
import ja va.util.concurrent.ExecutionException;
import ja va.util.concurrent.ExecutorService;
import ja va.util.concurrent.Executors;
import ja va.util.concurrent.Future;

/**
 * 计算指定范围内所有的质数
 */
public class Test03_ParallelComputing {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 单线程计算,耗时约12秒
        long start = System.currentTimeMillis();
        List list = getPrime(1, 500000);
        long end = System.currentTimeMillis();
        System.out.println(end - start);

        // 使用固定线程池(6个线程)并行计算,耗时缩短到约6秒
        ExecutorService service = Executors.newFixedThreadPool(6);

        MyTask taskA = new MyTask(1, 200000);
        MyTask taskB = new MyTask(200001, 350000);
        MyTask taskC = new MyTask(350001, 450000);
        MyTask taskD = new MyTask(450001, 500000);

        Future> futureA = service.submit(taskA);
        Future> futureB = service.submit(taskB);
        Future> futureC = service.submit(taskC);
        Future> futureD = service.submit(taskD);

        start = System.currentTimeMillis();
        List listA = futureA.get();
        List listB = futureB.get();
        List listC = futureC.get();
        List listD = futureD.get();
        end = System.currentTimeMillis();
        System.out.println(end - start);

        service.shutdown();
    }

    static class MyTask implements Callable> {
        int start, end;

        public MyTask(int start, int end) {
            this.start = start;
            this.end = end;
        }

        @Override
        public List call() throws Exception {
            return getPrime(start, end);
        }
    }

    public static boolean isPrime(int number) {
        for (int i = 2; i < number / 2; i++) {
            if (number % i == 0) {
                return false;
            }
        }
        return true;
    }

    public static List getPrime(int start, int end) {
        List list = new ArrayList<>();
        for (int i = start; i <= end; i++) {
            if (isPrime(i)) {
                list.add(i);
            }
        }
        return list;
    }
}

后记

目前仅为初步的知识点整理,后续会持续更新,加入更深入的理解与实践经验,不断完善这份多线程学习笔记。

来源:https://developer.aliyun.com/article/704800
上一篇年3款口碑最佳中小企业商城小程序工具零代码SAAS与AI 下一篇水下目标检测数据集涵盖11类海洋渔具与垃圾
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Claude Code 必知的14个高效工作流,让你的开发效率提升300%
AI教程 · 2026-07-09

Claude Code 必知的14个高效工作流,让你的开发效率提升300%

Claude Code 常用工作流 先分享几项核心判断:Claude Code 真正强大的地方,并非仅仅在于它能编写代码——而是它让“编码”这件事本身变得更加高效且可控。你大概率遇到过这类场景:接手一个陌生项目,花了一整天才能理清架构;线上出现报错,翻遍日志也找不到根本原因;想要重构遗留代码,又担心

阿里云通义AIGC平台完全指南:设计师AI生产力革命
AI教程 · 2026-07-09

阿里云通义AIGC平台完全指南:设计师AI生产力革命

一、写在前面:为什么设计师需要关注AIGC? 如果你还在手动一张一张制作海报、反复修改客户口中“感觉不对”的配色方案、为电商详情页准备几十张不同场景的产品图——那么你一定经历过这些痛点: 创意瓶颈:脑海中有画面,但手绘无法呈现 重复劳动:调整尺寸、更换背景、批量生成变体消耗了大量时间精力 成本焦虑:

零基础毕设代码二次开发:3文件定位法及Vue/Java修改对照表
AI教程 · 2026-07-09

零基础毕设代码二次开发:3文件定位法及Vue/Java修改对照表

每年一到毕业季,计算机专业的同学总会陷入一个共同的怪圈:从 GitHub 上扒下来一套代码,或者用 AI 生成一个项目,看起来挺完整的,可导师一句“加个筛选条件”或“换个页面颜色”,瞬间就懵了——不敢改,不会改,怕改崩。是不是很熟悉? 一、为什么AI生成的毕设代码你 "不敢改 "? 1 1 毕业生的三大

反向海淘订单系统:状态机与分布式事务实战设计
AI教程 · 2026-07-09

反向海淘订单系统:状态机与分布式事务实战设计

先分享一个反直觉的结论:反向海淘订单管理的真正挑战,往往不在于业务逻辑本身,而在于状态流转。一个订单的生命周期拉长到跨国运输,中间涉及的环节多、系统多、参与者多,状态稍有错乱就可能引发连锁事故。Taocarts团队在实践中踩了不少坑,最终沉淀下来的这套状态机与分布式事务方案,成功解决了这一复杂难题。

AI并未抢走程序员饭碗而是更新了编程菜单
AI教程 · 2026-07-09

AI并未抢走程序员饭碗而是更新了编程菜单

AI并未大规模替代程序员,而是改变了职业结构。重复性编码岗位需求下降35%至15%,而AIAgent开发等岗位需求激增187%。开发者焦虑从“被替代”转向“跟不上变化”,60%程序员已使用AI辅助编程。人的核心价值转向架构设计、技术决策和审查AI生成代码,AI技能带来16%薪资溢价。