第62章

Java多线程编程

掌握线程创建、同步机制、线程池使用和并发编程最佳实践

学习目标

线程创建方式详解

Java提供了多种创建线程的方式,每种方式都有其特定的优势和适用场景。理解这些方式的区别对于编写高效的多线程程序至关重要。

继承Thread类

语法示例:
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程执行中...");
    }
}

// 使用
MyThread thread = new MyThread();
thread.start();
  • 简单直观,易于理解
  • 适合简单的线程任务
  • 无法继承其他类(单继承限制)
  • 线程和任务耦合度高

实现Runnable接口

语法示例:
class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println("任务执行中...");
    }
}

// 使用
Thread thread = new Thread(new MyTask());
thread.start();
  • 避免单继承限制
  • 任务与线程分离,复用性好
  • 支持多个线程执行同一任务
  • 推荐的线程创建方式

实现Callable接口

语法示例:
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "任务执行结果";
    }
}

// 使用
FutureTask<String> task = new FutureTask<>(new MyCallable());
Thread thread = new Thread(task);
thread.start();
String result = task.get(); // 获取结果
  • 支持返回值
  • 可以抛出异常
  • 配合Future使用
  • 适合需要结果的任务

同步机制详解

在多线程环境中,同步机制是确保数据一致性和避免竞态条件的关键。Java提供了多种同步机制来满足不同的需求。

同步机制 特点 适用场景 性能影响
synchronized 内置锁,可重入 简单的同步需求 中等
volatile 保证可见性,禁止重排序 简单状态标记
ReentrantLock 显式锁,功能丰富 复杂的锁控制 中等
ReadWriteLock 读写分离锁 读多写少场景 低(读操作)
Atomic类 原子操作,无锁 简单的原子更新 很低

线程池详解

线程池是管理和复用线程的重要工具,能够显著提高应用程序的性能和资源利用率。

线程池的核心参数

  • corePoolSize:核心线程数,即使空闲也会保留
  • maximumPoolSize:最大线程数
  • keepAliveTime:非核心线程的空闲存活时间
  • workQueue:任务队列,存储等待执行的任务
  • threadFactory:线程工厂,用于创建新线程
  • handler:拒绝策略,处理无法执行的任务
ThreadPoolExecutor示例:
// 创建自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,                      // 核心线程数
    10,                     // 最大线程数
    60L,                    // 空闲存活时间
    TimeUnit.SECONDS,       // 时间单位
    new LinkedBlockingQueue<>(100),  // 任务队列
    Executors.defaultThreadFactory(), // 线程工厂
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

// 提交任务
executor.submit(() -> {
    System.out.println("任务执行中:" + Thread.currentThread().getName());
});

// 关闭线程池
executor.shutdown();

完整代码示例

ThreadBasics.java - 线程基础示例
/**
 * Java多线程基础示例
 * 演示三种线程创建方式的使用
 * 
 * @author Java学习者
 * @version 1.0
 * @since 2024-01-05
 */
public class ThreadBasics {
    
    public static void main(String[] args) {
        System.out.println("=== Java多线程编程示例 ===");
        
        // 方式1:继承Thread类
        demonstrateThreadClass();
        
        // 方式2:实现Runnable接口
        demonstrateRunnableInterface();
        
        // 方式3:实现Callable接口
        demonstrateCallableInterface();
    }
    
    /**
     * 演示继承Thread类的方式
     */
    private static void demonstrateThreadClass() {
        System.out.println("\n1. 继承Thread类创建线程:");
        
        MyThread thread = new MyThread("Thread-1");
        thread.start();
        
        try {
            thread.join(); // 等待线程执行完成
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    /**
     * 演示实现Runnable接口的方式
     */
    private static void demonstrateRunnableInterface() {
        System.out.println("\n2. 实现Runnable接口创建线程:");
        
        MyTask task = new MyTask("Task-1");
        Thread thread = new Thread(task, "Thread-2");
        thread.start();
        
        try {
            thread.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    /**
     * 演示实现Callable接口的方式
     */
    private static void demonstrateCallableInterface() {
        System.out.println("\n3. 实现Callable接口创建线程:");
        
        MyCallable callable = new MyCallable("Callable-1");
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask, "Thread-3");
        thread.start();
        
        try {
            String result = futureTask.get(); // 获取执行结果
            System.out.println("任务执行结果:" + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
💻 查看完整代码 - 在线IDE体验

多线程最佳实践

编程实践原则

推荐做法

// 使用线程池管理线程
ExecutorService executor = Executors.newFixedThreadPool(5);

// 合理设置线程名称
Thread.currentThread().setName("DataProcessor-" + id);

// 正确处理中断
if (Thread.currentThread().isInterrupted()) {
    return; // 响应中断
}

// 使用并发工具类
CountDownLatch latch = new CountDownLatch(3);
  • 优先使用线程池而非直接创建线程
  • 合理设置线程池参数
  • 正确处理线程中断
  • 使用并发工具类简化编程
  • 避免过度同步

避免的做法

// 避免:频繁创建线程
for (int i = 0; i < 1000; i++) {
    new Thread(() -> doWork()).start();
}

// 避免:忽略异常
try {
    thread.join();
} catch (InterruptedException e) {
    // 空catch块
}

// 避免:不合理的锁粒度
synchronized (this) {
    // 大量耗时操作
}
  • 频繁创建和销毁线程
  • 忽略InterruptedException
  • 过粗或过细的锁粒度
  • 在循环中使用Thread.sleep()
  • 不合理的线程数量设置

性能优化建议

常见性能问题

  • 线程数量过多导致上下文切换开销
  • 锁竞争激烈影响并发性能
  • 内存可见性问题导致的性能损失
  • 不合理的任务分配策略
  • 资源泄露和死锁问题

优化策略

  • 合理设置线程数:CPU密集型任务使用CPU核心数,IO密集型任务可适当增加
  • 减少锁竞争:使用读写锁、分段锁等技术
  • 选择合适的数据结构:使用ConcurrentHashMap等并发容器
  • 避免伪共享:合理设计数据结构布局
  • 监控和调优:使用JVM工具监控线程状态

本章小结