Java 多线程实现方式(原理+实战+对比)

我会从基础实现进阶用法,系统讲解Java多线程的所有核心实现方式,包括每种方式的原理、代码示例、适用场景和优缺点,新手也能快速上手。

一、核心实现方式(官方标准+实际常用)

Java中多线程的实现本质只有两种核心方式(Java官方定义),但实际开发中衍生出3种常用形式,先明确分类:

实现类型 核心原理 JDK版本 核心特点
继承Thread类 重写run()方法,线程执行体 1.0+ 简单,单继承限制
实现Runnable接口 重写run(),解耦任务与线程 1.0+ 无单继承限制,推荐
实现Callable+FutureTask 有返回值、可抛异常的Runnable 1.5+ 能获取执行结果,处理异常
线程池(Executor框架) 复用线程,管理任务执行 1.5+ 高性能,可控,生产首选
定时器(Timer/TimerTask) 定时/周期性执行任务 1.3+ 简单定时任务,单线程

二、方式1:继承Thread类(基础)

原理

Thread类是Java线程的核心类,继承它并重写run()方法(线程执行体),调用start()方法启动线程(JVM会调用run())。

代码示例

// 1. 继承Thread类
class MyThread extends Thread {
    // 重写run():线程要执行的任务
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            // Thread.currentThread().getName() 获取当前线程名
            System.out.println(Thread.currentThread().getName() + " 执行:" + i);
            try {
                Thread.sleep(100); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        // 2. 创建线程实例
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        
        // 3. 设置线程名(可选)
        thread1.setName("线程1");
        thread2.setName("线程2");
        
        // 4. 启动线程(必须调用start(),而非直接run())
        thread1.start();
        thread2.start();
        
        // 注意:直接调用run()会以主线程执行,不是多线程!
        // thread1.run(); // 错误用法
    }
}

输出结果(示例)

线程1 执行:0
线程2 执行:0
线程1 执行:1
线程2 执行:1
...

优缺点

  • ✅ 优点:代码简单,直接操作线程对象;
  • ❌ 缺点:Java单继承限制,任务与线程耦合(无法复用任务)。

三、方式2:实现Runnable接口(推荐基础方式)

原理

将“任务逻辑”与“线程对象”解耦:Runnable封装任务(重写run()),Thread负责执行任务(传入Runnable实例)。

代码示例

// 1. 实现Runnable接口,封装任务
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " 执行:" + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class RunnableDemo {
    public static void main(String[] args) {
        // 2. 创建任务实例
        MyRunnable task = new MyRunnable();
        
        // 3. 创建线程,传入任务
        Thread thread1 = new Thread(task, "线程1");
        Thread thread2 = new Thread(task, "线程2");
        
        // 4. 启动线程
        thread1.start();
        thread2.start();
    }
}

进阶:匿名内部类/Lambda简化(JDK8+)

public class RunnableLambdaDemo {
    public static void main(String[] args) {
        // Lambda表达式简化Runnable(无需定义类)
        Runnable task = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + " 执行:" + i);
                try { Thread.sleep(100); } catch (InterruptedException e) {}
            }
        };
        
        new Thread(task, "Lambda线程1").start();
        new Thread(task, "Lambda线程2").start();
    }
}

优缺点

  • ✅ 优点:无单继承限制,任务与线程解耦(可复用任务);
  • ❌ 缺点:run()无返回值,无法抛出检查异常(只能捕获)。

四、方式3:实现Callable+FutureTask(有返回值)

原理

Callable是Runnable的增强版:

  • call()方法有返回值(泛型);
  • call()可抛出检查异常;
  • FutureTask封装Callable,获取执行结果(get())。

代码示例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

// 1. 实现Callable接口,指定返回值类型
class MyCallable implements Callable<Integer> {
    private int num;
    
    public MyCallable(int num) {
        this.num = num;
    }

    // 有返回值、可抛异常的任务
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= num; i++) {
            sum += i;
            System.out.println(Thread.currentThread().getName() + " 计算:" + i);
            Thread.sleep(100);
        }
        return sum; // 返回计算结果
    }
}

public class CallableDemo {
    public static void main(String[] args) {
        // 2. 创建Callable任务
        Callable<Integer> callable = new MyCallable(5);
        
        // 3. FutureTask封装Callable(既是Runnable,又是Future)
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        
        // 4. 启动线程
        Thread thread = new Thread(futureTask, "计算线程");
        thread.start();
        
        // 5. 获取任务结果(get()会阻塞,直到任务完成)
        try {
            Integer result = futureTask.get();
            System.out.println("计算结果:" + result); // 输出15
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

核心方法说明

  • futureTask.get():阻塞获取返回值;
  • futureTask.cancel(boolean mayInterruptIfRunning):取消任务;
  • futureTask.isDone():判断任务是否完成。

优缺点

  • ✅ 优点:有返回值、可处理异常;
  • ❌ 缺点:get()方法会阻塞,需合理处理超时。

五、方式4:线程池(Executor框架)(生产首选)

原理

线程池复用线程(避免频繁创建/销毁线程的开销),管理任务队列,控制并发数,是生产环境的最优选择。

核心线程池类型(JDK提供)

线程池类型 核心特点 适用场景
FixedThreadPool 固定核心线程数,无最大线程数 稳定的并发任务
CachedThreadPool 核心线程0,最大线程无限 短期、高频小任务
SingleThreadExecutor 单线程执行 顺序执行任务
ScheduledThreadPool 定时/周期性执行 定时任务、延迟任务

代码示例:固定线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 1. 创建固定线程池(核心线程数=2)
        ExecutorService executor = Executors.newFixedThreadPool(2);
        
        // 2. 提交任务(Runnable)
        for (int i = 0; i < 5; i++) {
            int taskNum = i;
            executor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行任务" + taskNum);
                try { Thread.sleep(200); } catch (InterruptedException e) {}
            });
        }
        
        // 3. 提交Callable任务(有返回值)
        executor.submit(() -> {
            int sum = 0;
            for (int i = 1; i <= 3; i++) sum += i;
            System.out.println("Callable任务结果:" + sum);
            return sum;
        });
        
        // 4. 关闭线程池(生产中建议优雅关闭)
        executor.shutdown(); // 等待所有任务完成后关闭
        // executor.shutdownNow(); // 立即关闭,中断正在执行的任务
    }
}

进阶:自定义线程池(推荐生产使用)

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class CustomThreadPoolDemo {
    public static void main(String[] args) {
        // 自定义线程池参数(核心池大小、最大池大小、空闲时间、队列、拒绝策略)
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, // 核心线程数
                4, // 最大线程数
                60, // 空闲线程存活时间
                TimeUnit.SECONDS, // 时间单位
                new ArrayBlockingQueue<>(2), // 任务队列(容量2)
                new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略(主线程执行)
        );
        
        // 提交任务
        for (int i = 0; i < 7; i++) {
            int taskNum = i;
            executor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行任务" + taskNum);
                try { Thread.sleep(100); } catch (InterruptedException e) {}
            });
        }
        
        executor.shutdown();
    }
}

优缺点

  • ✅ 优点:复用线程、控制并发、管理任务、高性能;
  • ❌ 缺点:需合理配置参数(核心数、队列、拒绝策略)。

六、方式5:定时器(Timer/TimerTask)(定时任务)

原理

Timer是Java早期的定时任务工具,单线程执行TimerTask(实现Runnable),支持延迟/周期性执行。

代码示例

import java.util.Timer;
import java.util.TimerTask;

public class TimerDemo {
    public static void main(String[] args) {
        Timer timer = new Timer("定时任务线程");
        
        // 延迟1秒执行,每2秒重复执行
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " 执行定时任务:" + System.currentTimeMillis());
            }
        }, 1000, 2000);
        
        // 5秒后取消定时器
        try {
            Thread.sleep(5000);
            timer.cancel();
            System.out.println("定时器已取消");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

注意事项

  • ❌ Timer是单线程,一个任务异常会导致所有任务终止;
  • ✅ 生产中推荐用ScheduledThreadPoolExecutor替代Timer。

七、核心对比与选型建议

实现方式 返回值 异常处理 单继承限制 性能 适用场景
继承Thread 只能捕获 一般 简单demo,不推荐生产
实现Runnable 只能捕获 较好 无返回值的普通任务
Callable+FutureTask 可抛出 较好 需返回值/处理异常的任务
线程池 可选 可处理 最优 生产环境所有场景(推荐)
Timer/TimerTask 只能捕获 较差 简单定时任务(替代:ScheduledThreadPool)

选型核心原则

  1. 生产环境优先用线程池(性能、可控性最优);
  2. 需返回值用Callable + 线程池
  3. 简单demo可用Runnable/Thread;
  4. 定时任务用ScheduledThreadPoolExecutor,而非Timer。

总结

  1. Java多线程核心实现是「继承Thread」和「实现Runnable」,其余是衍生增强;
  2. 「Callable+FutureTask」解决返回值/异常问题,「线程池」解决性能/管理问题;
  3. 生产环境首选线程池(自定义参数更可控),避免直接创建Thread;
  4. 核心区别:返回值、异常处理、线程复用,选型需结合业务场景。
posted @ 2026-03-06 17:18  七星6609  阅读(13)  评论(0)    收藏  举报