Java 多线程基础

Java 多线程基础

核心概念区分

程序、进程、线程

  • 程序:静止的应用程序,是指令和数据的集合。
  • 进程:运行中的应用程序,是操作系统资源分配的基本单位,在内存中占据独立空间。
  • 线程:进程内的独立执行单元,是 CPU 调度的基本单位。一个进程可包含多个线程,线程共享进程的资源。

多线程的优点

  • 业务拆分更灵活,提升系统并发处理能力。
  • 多处理器环境下可实现并行执行,单处理器环境下通过时间分片实现并发,提高资源利用率。
  • 程序响应更快、交互性更强,部分场景下(如 IO 密集型任务)单处理器上也能提升执行效率。
  • Java 内置完善的多线程支持,包括线程创建、运行及资源冲突控制。

image

在 Java 中,每个并发任务需封装为 Runnable 接口的实例(可运行对象),线程则是执行这些任务的载体。

Java 实现多线程的四种方式

方式一:实现 Runnable 接口(传统)

通过实现 Runnable 接口定义任务,再将任务传入 Thread 实例启动线程,任务与线程分离,是推荐的传统方式。

代码示例

package com.thread;

/**
 * @author Jing61
 */
public class TaskThreadDemo {
    public static void main(String[] args) {
        // 创建线程1对象
        PrintLetterTask task1 = new PrintLetterTask('A', 100);
        Thread thread1 = new Thread(task1);
        // 创建线程2对象
        Thread thread2 = new Thread(new PrintLetterTask('B', 100));
        // 创建线程3对象
        Thread thread3 = new Thread(new PrintNumberTask(100));
        // 启动线程(告诉操作系统,该线程已就绪,等待操作系统分配资源,由操作系统调度执行)
        thread1.start();
        // 启动线程
        thread2.start();
        // 启动线程
        thread3.start();
    }
}

/**
 * 创建线程的方式:实现Runnable接口
 * 该任务完成打印特定字母times次
 */
class PrintLetterTask implements Runnable {
    private char letter;
    private int times;

    public PrintLetterTask(char letter, int times) {
        this.letter = letter;
        this.times = times;
    }

    @Override
    public void run() {
        for (int i = 0; i < times; i++) {
            System.out.print(letter);
        }
        System.out.println();
    }
}

/**
 * 该任务连续打印数字
 */
class PrintNumberTask implements Runnable {
    private int lastNumber;

    public PrintNumberTask(int number) {
        this.lastNumber = number;
    }

    @Override
    public void run() {
        for (int i = 1; i <= lastNumber; i++) {
            System.out.print(i + " ");
        }
        System.out.println();
    }
}

方式二:继承 Thread 类(传统)

Thread 类本身实现了 Runnable 接口,可通过继承 Thread 并重写 run 方法定义线程,但不推荐使用。

缺点

  • 任务与线程机制耦合,不符合“单一职责”设计原则。
  • 受 Java 单继承限制,无法再继承其他类。

代码示例

package com.thread;

/**
 * @author Jing61
 * 会出现问题——线程同步问题(会出现不同线程卖同一张票的问题),本文暂不做讲解。
 * 此处仅展示如何执行
 */
public class CustomThreadClient {
    public static void main(String[] args) {
        // 5 个窗口卖票
        for (int i = 0; i < 5; i++) {
            Thread thread = new CustomThread();
            thread.start();
        }
    }
}

/**
 * 继承Thread类,覆盖run方法
 */
class CustomThread extends Thread {
    private int ticket = 100;

    @Override
    public void run() {
        while (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "正在卖出编号为" + ticket + "的票");
            ticket--;
        }
    }
}

方式三:线程池(高效管理多任务)

对于大量任务,频繁创建销毁线程会降低效率,线程池通过复用线程、控制并发数优化性能。Java 提供 Executor 接口执行任务,ExecutorService 接口(Executor 子接口)管理任务。

线程池创建方式(Executors 静态方法)

方法 核心特性 适用场景
newFixedThreadPool(int n) 固定线程数,线程可重用 任务量稳定、需控制并发数的场景
newCachedThreadPool() 缓存线程池,无固定大小,60秒空闲线程销毁 大量短期小任务
newSingleThreadExecutor() 单线程池,线程异常时自动替换 需串行执行任务的场景
newScheduledThreadPool(int corePoolSize) 定时/延迟执行,核心线程数固定 定时任务、延迟任务
newSingleThreadScheduledExecutor() 单线程定时池 串行执行的定时/延迟任务
newWorkStealingPool(int parallelism) 支持并行级别的线程池,多队列减少竞争 高并行度的任务;无参版本默认以 CPU 核心数为并行度

代码示例

package com.thread;

import java.util.concurrent.Executors;

/**
 * @author Jing61
 */
public class ExecutorDemo {
    public static void main(String[] args) {
        // 创建线程池 ExecutorService
        // var executor = Executors.newFixedThreadPool(3); // 固定3个线程
        // var executor = Executors.newCachedThreadPool(); // 缓存线程池
        // 创建单线程池,线程异常时自动替换
        var executor = Executors.newSingleThreadExecutor();
        
        // 向线程池中提交任务(线程池会分配线程执行任务)
        executor.execute(new PrintLetterTask('A', 10));
        executor.execute(new PrintLetterTask('B', 10));
        executor.execute(new PrintLetterTask('C', 10));
        executor.execute(new PrintLetterTask('D', 10));
        executor.execute(new PrintNumberTask(50));

        // 关闭线程池(不再接受新的任务,等待所有任务执行完毕)
        executor.shutdown();
        System.out.println("线程池已关闭");
    }
}

方式四:Callable + Future(带返回值的线程)

Callable 接口是 Runnable 的增强版,call() 方法可返回任务执行结果、抛出异常。结合 Future 接口可跟踪异步计算结果,FutureTaskFuture 的实现类(同时实现 Runnable 接口),可作为 Thread 的目标对象或提交给线程池。

核心组件说明

  • Callable:定义带返回值的任务,核心方法 call()
  • Future:管理异步结果,提供 get()(阻塞获取结果)、cancel()(取消任务)、isDone()(判断任务是否完成)等方法。
  • FutureTask:包装 Callable/Runnable 对象,兼具 Runnable 和 Future 特性。

代码示例

package com.thread;

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

/**
 * @author Jing61
 */
public class CallableAndFutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建FutureTask,包装Callable任务(返回值类型为String)
        FutureTask<String> task = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("call方法执行了");
                Thread.sleep(5000); // 模拟任务执行耗时
                return "hello world";
            }
        });

        var executor = Executors.newSingleThreadExecutor();
        var future = executor.submit(task);
        
        // 获取call方法返回值(阻塞,直到任务执行完毕)
        System.out.println(task.get());
        System.out.println(future.isDone()); // 判断线程是否运行完毕
        
        executor.shutdown();
    }
}
posted @ 2025-11-13 12:27  Jing61  阅读(3)  评论(0)    收藏  举报