一、先搞懂:啥是多线程?

1. 生活例子

  • 单线程:你一个人做饭 —— 先买菜→洗菜→切菜→炒菜→洗碗,一步做完才能做下一步(效率低);
  • 多线程:你和家人一起做饭 —— 你切菜、妈妈炒菜、爸爸洗碗,同时干活(效率翻倍)。

2. 核心定义

  • 线程:程序里的 “最小干活单元”(比如 “切菜”“炒菜” 都是一个线程);
  • 多线程:一个程序同时跑多个 “干活单元”,不用等一个做完再做下一个(比如视频软件同时 “下载视频”+“播放画面”+“发弹幕”)。

3. 为啥要用多线程?

  • 提高效率:比如下载 3 个文件,单线程要等 1 个下完再下下一个,多线程能同时下;
  • 应对多任务:比如游戏里同时 “显示画面”“播放音效”“处理玩家操作”,少一个线程就卡了。

二、多线程的 “核心组件”

1. 线程 “载体”:Thread 类

  • 作用:每个线程都是Thread类的 “具体对象”(就像每个干活的人都是 “人类” 的具体个体);
  • 必记操作(干活的流程)
    • new Thread()创建线程(招聘一个人);
    • start()启动线程(让这个人开始干活,底层会自动调用run()方法,千万别直接写run()!);
    • run()线程要做的具体活(比如 “切菜”,需要我们自己写清楚);
    • 常用小工具
      • sleep(1000)让线程休息 1 秒(比如切菜累了,歇 1 秒再切);
      • getName()获取线程名字(比如 “切菜线程”“炒菜线程”);
      • getId()获取线程唯一编号(比如给每个人编个工号)。

2. 任务 “说明书”:Runnable 接口

  • 问题:如果一个类已经继承了别的类(比如 “学生类” 继承了 “人类”),就不能再继承Thread类了(Java 只能单继承);
  • 解决方案:Runnable接口 —— 专门写 “干活的内容”,再交给Thread(人)去执行;
  • 作用:把 “人” 和 “活” 分开,灵活搭配(比如一份 “洗碗清单”,可以让爸爸做,也可以让妈妈做)。

3. 共享资源 “保护锁”

  • 问题:多个线程抢一个资源(比如 3 个人同时用一台冰箱拿东西),会乱套(比如冰箱门被同时拉,东西掉地上)—— 这叫 “线程安全问题”;
  • 解决方案:给共享资源 “加锁”,只有拿到钥匙的线程才能用(比如冰箱钥匙只有一把,谁拿到谁用,用完再还回来);
  • 用法:简单粗暴,给 “操作共享资源的代码” 套一层 synchronized,比如:

三、多线程 3 种实现方式

1. 方式 1:继承 Thread 类

  • 步骤:① 新建类继承Thread;② 重写run()方法(写干活内容);③ 创建对象,调用start()启动;
  • 示例(打印 1-5):

2. 方式 2:实现 Runnable 接口

  • 步骤:① 新建类实现Runnable;② 重写run()方法(写干活内容);③ 把这个类的对象传给Thread,调用start()
  • 示例(打印 “我在干活”):

3. 方式 3:线程协作 

  • 场景:比如让线程 1 先打印 1,线程 2 再打印 2,线程 1 再打印 3(之前的交替打印 1-20);
  • 核心逻辑:线程之间 “打招呼”—— 该我干就干,不该我干就等着,干完叫下一个;
  • 必记方法(必须在synchronized代码块里用):
    • wait()让当前线程等着(比如线程 2 要打印 2,但现在是 1,就等着);
    • notify()叫醒一个等着的线程(线程 1 打印完 1,叫醒线程 2 打印 2);
  • 简单示例(交替打印):

四、线程的 “一生”

  1. 新建状态(New):new Thread()—— 刚创建线程,还没启动(像婴儿刚出生,没开始干活);
  2. 就绪状态(Runnable):调用start()后 —— 线程等着 CPU 分配任务(像成年人等着上班,随时能开工);
  3. 运行状态(Running):CPU 选中线程,执行run()—— 线程正在干活(像成年人正在上班);
  4. 阻塞状态(Blocked):线程暂停执行(比如sleep()wait(),或者等锁)—— 像成年人上班中途休息、等电梯;
  5. 终止状态(Terminated):run()方法执行完 —— 线程 “下班”,生命周期结束(像成年人退休)。

状态变化图:

新建(new) → 调用start() → 就绪 → CPU 选中 → 运行 → 任务完成 → 终止(运行中可能因sleep()/wait()进入阻塞,阻塞结束后回到就绪

五、必避的 4 个坑

  1. 直接调用run()方法:比如thread.run()—— 这不是启动多线程,只是普通方法调用(相当于让 “人” 拿着清单看一眼,没真干活),必须用start()
  2. 共享变量不加锁:比如多个线程改同一个int num—— 会出现 “数字错乱”(比如本来该加 1,结果加了 2),一定要用synchronized锁起来;
  3. 以为线程会按顺序执行:默认情况下,线程执行顺序由 CPU 决定(比如你和妈妈同时干活,谁先做完不一定),想控序必须用wait/notify
  4. 死锁:比如线程 A 拿着钥匙 1 等钥匙 2,线程 B 拿着钥匙 2 等钥匙 1—— 互相卡死,程序停住(避免方法:不要让线程互相等对方的锁)。

六、核心总结

  • 多线程:多个 “干活单元” 同时跑,提高效率;
  • 实现:继承 Thread(简单)、实现 Runnable(灵活)、wait/notify(控序);
  • 安全:共享资源加synchronized锁;
  • 状态:新建→就绪→运行→阻塞→终止。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐