第一部分:线程创建方式

掌握Java中创建线程的4种方式

学习目标

理解并掌握继承Thread类、实现Runnable接口、实现Callable接口和使用线程池创建线程的方式

1. 继承Thread类

public class MyThread extends Thread { @Override public void run() { System.out.println("线程运行: " + this.getName()); } public static void main(String[] args) { MyThread t = new MyThread(); t.start(); // 启动线程 } }

特点: 简单直接,但无法继承其他类

2. 实现Runnable接口

public class MyRunnable implements Runnable { @Override public void run() { System.out.println("线程运行: " + Thread.currentThread().getName()); } public static void main(String[] args) { Thread t = new Thread(new MyRunnable()); t.start(); } }

特点: 解耦任务与线程,可继承其他类

3. 实现Callable接口

import java.util.concurrent.*; public class MyCallable implements Callable { @Override public String call() throws Exception { return "Callable执行结果: " + Thread.currentThread().getName(); } public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new MyCallable()); System.out.println(future.get()); executor.shutdown(); } }

特点: 支持返回值,可抛出异常

4. 使用线程池

import java.util.concurrent.*; public class ThreadPoolExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(3); for (int i = 0; i < 5; i++) { executor.execute(() -> { System.out.println("线程池执行: " + Thread.currentThread().getName()); }); } executor.shutdown(); } }

特点: 高效资源管理,避免频繁创建销毁

创建方式对比

方式 优点 缺点 适用场景
继承Thread 简单直接 无法继承其他类 简单任务
实现Runnable 解耦任务与线程 无返回值 大多数场景
实现Callable 支持返回值 使用复杂 需要返回结果
线程池 高效资源管理 配置复杂 生产环境

第二部分:线程生命周期

理解线程的6种状态及其转换

学习目标

掌握线程的6种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED

NEW
新建
RUNNABLE
可运行
BLOCKED
阻塞
WAITING
等待
TIMED_WAITING
限时等待
TERMINATED
终止

NEW(新建)

线程被创建但未启动时的状态

创建方式: new Thread()

转换条件: 调用start()方法进入RUNNABLE状态

RUNNABLE(可运行)

线程正在运行或准备运行的状态

包含: 就绪(Ready)和运行中(Running)

转换条件: 时间片用完或主动放弃进入BLOCKED/WAITING

BLOCKED(阻塞)

等待获取锁时的状态

触发条件: 进入synchronized方法/块

转换条件: 获取到锁后进入RUNNABLE状态

WAITING(等待)

无限期等待其他线程操作

触发方法: Object.wait(), Thread.join()

转换条件: 被notify()/notifyAll()唤醒

TIMED_WAITING(限时等待)

有限时间等待状态

触发方法: Thread.sleep(), Object.wait(timeout)

转换条件: 时间结束或被唤醒

TERMINATED(终止)

线程执行结束后的状态

触发条件: run()方法执行完成

转换条件: 不可再启动

状态监控技巧

1. 使用jstack工具查看线程状态
2. 在IDEA中通过调试视图查看线程状态
3. Thread.getState()方法获取状态
4. 避免线程长时间处于BLOCKED/WAITING状态

第三部分:线程同步(synchronized)

掌握synchronized关键字的使用

学习目标

理解synchronized的原理、使用方式及常见问题

synchronized关键字

Java内置同步机制,简单易用

public class SynchronizedExample { private int count = 0; public synchronized void increment() { count++; } // 或使用同步块 public void increment2() { synchronized(this) { count++; } } }

优点: 自动释放锁,无需手动管理

缺点: 功能有限,无法中断等待锁的线程

锁升级过程

Java 6引入的锁优化机制

  • 偏向锁:适用于只有一个线程访问的场景
  • 轻量级锁:适用于线程交替执行的场景
  • 重量级锁:适用于多线程竞争的场景

优点: 根据竞争情况自动升级锁

缺点: 锁升级不可逆

常见错误

1. 锁对象错误:使用可变对象或基本类型作为锁
2. 锁范围过大:同步整个方法或大段代码
3. 死锁问题:多个线程互相持有对方需要的锁
4. 忘记释放锁:synchronized会自动释放

第四部分:Lock锁与ReentrantLock

掌握Lock接口的使用及ReentrantLock特性

学习目标

理解Lock接口的核心方法、ReentrantLock的可重入性、公平锁和非公平锁的区别

Lock接口核心方法

  • lock() - 获取锁
  • unlock() - 释放锁
  • tryLock() - 尝试获取锁
  • lockInterruptibly() - 可中断获取锁
  • newCondition() - 创建Condition对象

ReentrantLock使用

import java.util.concurrent.locks.*; public class LockExample { private final Lock lock = new ReentrantLock(); private int count = 0; public void increment() { lock.lock(); // 获取锁 try { count++; } finally { lock.unlock(); // 确保释放锁 } } }

公平锁与非公平锁

// 创建非公平锁(默认) Lock nonFairLock = new ReentrantLock(); // 创建公平锁 Lock fairLock = new ReentrantLock(true);

公平锁: 按照线程请求顺序获取锁

非公平锁: 允许插队,性能更高

Lock与synchronized对比

特性 synchronized Lock
实现方式 JVM内置 Java代码实现
锁获取 自动获取释放 手动获取释放
可中断 不支持 支持
公平锁 不支持 支持
超时获取 不支持 支持

第五部分:线程通信(wait/notify)

掌握线程间协作机制

学习目标

理解wait(), notify(), notifyAll()方法的使用及生产者-消费者模式

wait()方法

使当前线程进入等待状态,释放锁

使用条件: 必须在同步块内调用

notify()方法

唤醒在此对象监视器上等待的单个线程

使用条件: 必须在同步块内调用

notifyAll()方法

唤醒在此对象监视器上等待的所有线程

使用条件: 必须在同步块内调用

import java.util.LinkedList; import java.util.Queue; public class ProducerConsumer { private final Queue queue = new LinkedList<>(); private final int capacity = 5; private final Object lock = new Object(); public void produce() throws InterruptedException { int value = 0; while (true) { synchronized (lock) { while (queue.size() == capacity) { lock.wait(); // 等待不满 } queue.add(value++); lock.notifyAll(); // 通知不空 } } } public void consume() throws InterruptedException { while (true) { synchronized (lock) { while (queue.isEmpty()) { lock.wait(); // 等待不空 } int value = queue.poll(); lock.notifyAll(); // 通知不满 } } } }

常见错误

1. 在非同步块内调用wait/notify
2. 使用if而不是while检查条件
3. 忘记调用notify/notifyAll
4. 错误使用notify而不是notifyAll
5. 忽略InterruptedException

第六部分:线程池(ExecutorService)

掌握线程池的使用及核心参数

学习目标

理解线程池的工作原理、核心参数及常见线程池类型

核心参数

  • corePoolSize:核心线程数
  • maxPoolSize:最大线程数
  • keepAliveTime:空闲线程存活时间
  • workQueue:任务队列
  • threadFactory:线程工厂
  • handler:拒绝策略

常见线程池

  • newFixedThreadPool:固定大小线程池
  • newCachedThreadPool:可缓存线程池
  • newSingleThreadExecutor:单线程线程池
  • newScheduledThreadPool:定时任务线程池

拒绝策略

  • AbortPolicy:抛出RejectedExecutionException
  • CallerRunsPolicy:由调用线程执行任务
  • DiscardPolicy:直接丢弃任务
  • DiscardOldestPolicy:丢弃队列最前面的任务
import java.util.concurrent.*; public class CustomThreadPool { public static void main(String[] args) { int corePoolSize = 5; int maxPoolSize = 10; long keepAliveTime = 60; TimeUnit unit = TimeUnit.SECONDS; BlockingQueue workQueue = new ArrayBlockingQueue<>(100); ThreadFactory threadFactory = Executors.defaultThreadFactory(); RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler ); for (int i = 0; i < 15; i++) { executor.execute(() -> { System.out.println("任务执行: " + Thread.currentThread().getName()); }); } executor.shutdown(); } }

最佳实践

1. 使用ThreadPoolExecutor而不是Executors创建线程池
2. 根据任务类型设置合理的线程数
3. 使用有界队列防止内存溢出
4. 设置合理的拒绝策略
5. 使用shutdown()关闭线程池

第七部分:Callable与Future

掌握带返回值的异步任务处理机制

学习目标

理解Callable接口、Future接口及FutureTask的使用

Callable接口

带返回值的任务接口

import java.util.concurrent.*; public class MyCallable implements Callable { @Override public String call() throws Exception { return "Callable执行结果"; } }

特点: 支持返回值,可抛出异常

Future接口

表示异步计算的结果

ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new MyCallable()); // 获取结果(阻塞) String result = future.get(); // 取消任务 future.cancel(true); executor.shutdown();

特点: 可取消任务,可获取结果

FutureTask

Future接口的实现类

FutureTask futureTask = new FutureTask<>(new MyCallable()); new Thread(futureTask).start(); // 获取结果 String result = futureTask.get();

CompletableFuture

Java 8增强的异步编程API

CompletableFuture future = CompletableFuture.supplyAsync(() -> { return "异步任务结果"; }); future.thenAccept(result -> { System.out.println("处理结果: " + result); });

第八部分:综合测试

通过测试巩固多线程知识

问题1:线程创建方式

以下哪种方式不是Java中创建线程的正确方式?
  • A. 继承Thread类
  • B. 实现Runnable接口
  • C. 实现Callable接口
  • D. 使用new ThreadPool()
正确答案:D
正确创建线程池的方式是使用Executors工厂类或ThreadPoolExecutor构造函数

问题2:线程状态

调用Thread.sleep(1000)方法会使线程进入什么状态?
  • A. NEW
  • B. RUNNABLE
  • C. BLOCKED
  • D. TIMED_WAITING
正确答案:D
Thread.sleep()方法会使线程进入TIMED_WAITING状态

问题3:synchronized

关于synchronized关键字,以下说法错误的是?
  • A. 可以修饰方法
  • B. 可以修饰代码块
  • C. 支持可重入
  • D. 支持中断等待
正确答案:D
synchronized不支持中断等待,Lock接口才支持中断

问题4:Lock锁

使用ReentrantLock时,以下哪种方式可以避免死锁?
  • A. 使用tryLock()尝试获取锁
  • B. 使用lock()方法
  • C. 使用unlock()方法
  • D. 使用newCondition()方法
正确答案:A
tryLock()可以尝试获取锁,避免无限期等待

问题5:线程通信

在生产者-消费者模式中,当队列满时生产者应该调用什么方法?
  • A. notify()
  • B. notifyAll()
  • C. wait()
  • D. sleep()
正确答案:C
当队列满时,生产者应该调用wait()方法等待

问题6:线程池

以下关于线程池参数的说法正确的是?
  • A. corePoolSize必须大于maxPoolSize
  • B. keepAliveTime只适用于核心线程
  • C. workQueue可以是无界队列
  • D. 拒绝策略在队列满时触发
正确答案:D
拒绝策略在队列满且线程数达到maxPoolSize时触发
您的得分:0/6

知识总结

1. 线程创建:掌握4种创建方式及适用场景
2. 线程状态:理解6种状态及其转换条件
3. 线程同步:掌握synchronized和Lock的使用及区别
4. 线程通信:理解wait/notify机制及生产者-消费者模式
5. 线程池:掌握线程池参数配置及使用方式
6. Callable/Future:掌握带返回值的异步任务处理

Java多线程知识图谱

Java多线程核心知识图谱