Java中的线程详解

线程是Java并发编程的核心概念,是程序执行的最小单元。Java从最初版本就提供了线程支持,并随着发展不断增强了线程相关API。

一、线程基本概念

1. 线程与进程的区别

  • ​进程​​:操作系统资源分配的基本单位,有独立的内存空间
  • ​线程​​:CPU调度的基本单位,共享进程的内存空间

2. Java线程的特点

  • 每个Java程序至少有一个主线程(main线程)
  • 线程共享堆内存,但有独立的栈空间
  • Java线程调度由JVM和操作系统共同管理

二、线程创建方式

1. 继承Thread类

优点:

  • ​简单直接​​:只需重写run()方法即可
  • ​访问简单​​:可直接使用this获取当前线程对象
class MyThread extends Thread {
    public void run() {
        System.out.println("Thread running");
    }
}
// 使用
new MyThread().start();

缺点:

  • ​单继承限制​​:Java不支持多继承,影响扩展性
  • ​职责不单一​​:将任务与线程控制耦合在一起
  • ​资源浪费​​:每次新建任务需创建新线程对象

2. 实现Runnable接口

优点:

  • ​接口灵活​​:可实现多个接口,扩展性好
  • ​职责分离​​:任务逻辑与线程控制解耦
  • ​资源共享​​:适合多线程处理同一任务
class MyRunnable implements Runnable {
    public void run() {
        System.out.println("Task running");
    }
}
// 使用
new Thread(new MyRunnable()).start();

缺点:

  • ​无返回值​​:run()方法返回void
  • ​功能有限​​:缺少线程控制方法(如暂停、停止)
  • ​仍需创建Thread​​:本质上还是依赖Thread类

3. 实现Callable接口

优点:

  • ​支持返回值​​:通过Future获取计算结果
  • ​异常处理​​:可抛出checked exception
  • ​配合线程池​​:与ExecutorService完美集成
class MyCallable implements Callable<String> {
    public String call() throws Exception {
        return "Result";
    }
}
// 使用
FutureTask<String> future = new FutureTask<>(new MyCallable());
new Thread(future).start();
String result = future.get();

缺点:

  • ​使用复杂​​:需要配合Future/FutureTask使用
  • ​阻塞获取​​:future.get()会阻塞当前线程
  • ​仍需包装​​:单独使用时仍需转为Runnable

4. 使用线程池(ExecutorService)

优点:

  • ​资源复用​​:避免频繁创建销毁线程
  • ​管理方便​​:提供线程生命周期管理
  • ​功能丰富​​:支持定时/延时任务
  • ​负载控制​​:可限制最大并发数
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {
    System.out.println("Pool task");
});
executor.shutdown();

缺点:

  • ​配置复杂​​:需要合理设置参数(核心/最大线程数等)
  • ​资源泄漏风险​​:忘记关闭会导致线程泄漏
  • ​调试困难​​:线程池内部问题较难排查

综合对比表

创建方式返回值支持异常处理资源消耗扩展性适用场景
继承Thread简单演示/测试
实现Runnable大多数无返回值的线程任务
实现Callable✔️✔️需要返回结果或异常处理的场景
使用线程池✔️✔️最好生产环境高并发场景

三、线程生命周期

Java线程有以下6种状态(Thread.State枚举):

状态​​枚举值​​特征描述​​进入条件​​退出条件​​常用方法​
​NEWThread.State.NEW线程对象已创建但未启动通过new Thread()创建线程对象调用start()方法new Thread()
​RUNNABLEThread.State.RUNNABLE线程可能在运行或等待CPU时间片调用start()后自动进入
从阻塞/等待状态恢复
被系统调度器选中执行
主动让出CPU(yield()
进入同步阻塞/等待状态
start()
yield()
BLOCKED)Thread.State.BLOCKED等待获取监视器锁(同步锁)尝试进入synchronized代码块/方法但锁被占用成功获取对象锁synchronized
ReentrantLock.lock()
​WAITINGThread.State.WAITING无限期等待其他线程唤醒调用无超时参数的wait()
join()LockSupport.park()
被其他线程notify()/notifyAll()
目标线程终止(针对join()
被中断
Object.wait()
Thread.join()
LockSupport.park()
​TIMED_WAITINGThread.State.TIMED_WAITING有限时间等待调用带超时参数的sleep()
wait(timeout)
join(timeout)
超时时间到
被提前唤醒/中断
Thread.sleep()
Object.wait(timeout)
Thread.join(timeout)
​TERMINATED)Thread.State.TERMINATED线程执行完毕或异常终止run()方法正常结束
未捕获异常导致线程终止
无(最终状态)-

 

四、线程池

Java线程池详解

线程池是Java并发编程中一个非常重要的概念,它提供了一种高效管理线程的机制,可以避免频繁创建和销毁线程带来的性能开销。

4.1.线程池基本概念

线程池(Thread Pool)是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。

为什么需要线程池

  1. ​降低资源消耗​​:通过重复利用已创建的线程降低线程创建和销毁的开销
  2. ​提高响应速度​​:当任务到达时,任务可以不需要等待线程创建就能立即执行
  3. ​提高线程的可管理性​​:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性

4.2.Java线程池核心类

Java通过java.util.concurrent.Executors工厂类提供线程池的创建方法,底层都是通过ThreadPoolExecutor类实现的。

1. ThreadPoolExecutor构造方法

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
参数说明​
  1. corePoolSize

    • ​核心线程数​​:线程池中保持存活的最小线程数,即使这些线程处于空闲状态。
    • 当新任务提交时,如果当前线程数 < corePoolSize,线程池会创建新线程处理任务(即使有空闲线程)。
    • 默认情况下,核心线程会一直存活,除非设置 allowCoreThreadTimeOut(true)
  2. maximumPoolSize

    • ​最大线程数​​:线程池允许创建的最大线程数量。
    • 当任务队列已满且当前线程数 < maximumPoolSize,线程池会创建新线程处理任务。
    • 如果队列无界(如 LinkedBlockingQueue),此参数无效。
  3. keepAliveTime + unit

    • ​空闲线程存活时间​​:当线程数 > corePoolSize 时,空闲线程在终止前等待新任务的最长时间。
    • 时间单位由 unit 指定(如 TimeUnit.SECONDS)。
    • 若设置 allowCoreThreadTimeOut(true),核心线程也会受此限制。
  4. workQueue

    • ​任务队列​​:用于保存等待执行的任务的阻塞队列。常用实现:
      • ArrayBlockingQueue:有界队列,需指定容量。
      • LinkedBlockingQueue:无界队列(默认 Integer.MAX_VALUE),可能导致 OOM。
      • SynchronousQueue:不存储任务,直接提交给线程(需配合合理的 maximumPoolSize)。
      • PriorityBlockingQueue:带优先级的无界队列。
  5. threadFactory

    • ​线程工厂​​:用于创建新线程,可自定义线程名称、优先级、是否为守护线程等。
    • 默认使用 Executors.defaultThreadFactory(),创建的线程名类似 pool-N-thread-M
  6. handler

    • ​拒绝策略​​:当线程池和队列已满时的处理策略。常用实现:
      • AbortPolicy(默认):抛出 RejectedExecutionException
      • CallerRunsPolicy:由提交任务的线程直接执行任务。
      • DiscardPolicy:静默丢弃任务。
      • DiscardOldestPolicy:丢弃队列中最旧的任务,然后重试提交。
      • 自定义策略:实现 RejectedExecutionHandler 接口。

2. Executors提供的常用线程池

  1. ​FixedThreadPool​​:固定大小的线程池

    Executors.newFixedThreadPool(int nThreads)
  2. ​CachedThreadPool​​:可缓存的线程池

    Executors.newCachedThreadPool()
  3. ​SingleThreadExecutor​​:单线程的线程池

    Executors.newSingleThreadExecutor()
  4. ​ScheduledThreadPool​​:定时任务的线程池

    Executors.newScheduledThreadPool(int corePoolSize)

4.3.线程池工作原理

  1. 当提交一个新任务时:

    • 如果当前运行的线程数 < corePoolSize,则创建新线程执行任务
    • 如果运行的线程数 >= corePoolSize,则将任务放入workQueue
    • 如果workQueue已满且运行的线程数 < maximumPoolSize,则创建新线程执行任务
    • 如果workQueue已满且运行的线程数 >= maximumPoolSize,则执行拒绝策略
  2. 当线程空闲时间超过keepAliveTime时:

    • 如果当前线程数 > corePoolSize,则销毁该线程
    • 核心线程默认不会销毁,除非设置allowCoreThreadTimeOut为true

4.4.线程池任务队列

Java线程池常用的阻塞队列:

  1. ​ArrayBlockingQueue​​:基于数组的有界阻塞队列
  2. ​LinkedBlockingQueue​​:基于链表的阻塞队列(默认无界)
  3. ​SynchronousQueue​​:不存储元素的阻塞队列
  4. ​PriorityBlockingQueue​​:具有优先级的无界阻塞队列

4.5.拒绝策略

当线程池无法处理新任务时(队列满且线程数达到最大值),会执行拒绝策略:

  1. ​AbortPolicy​​(默认):直接抛出RejectedExecutionException异常
  2. ​CallerRunsPolicy​​:由调用线程处理该任务
  3. ​DiscardPolicy​​:直接丢弃任务
  4. ​DiscardOldestPolicy​​:丢弃队列中最老的任务,然后尝试重新提交当前任务

4.6.线程池使用示例

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, // 核心线程数
            4, // 最大线程数
            60, // 空闲线程存活时间
            TimeUnit.SECONDS, // 时间单位
            new ArrayBlockingQueue<>(10), // 任务队列
            Executors.defaultThreadFactory(), // 线程工厂
            new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
        );
        
        // 提交任务
        for (int i = 0; i < 15; i++) {
            final int taskNum = i;
            executor.execute(() -> {
                System.out.println("执行任务 " + taskNum + ",线程:" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        
        // 关闭线程池
        executor.shutdown();
    }
}

4.7.线程池最佳实践

  1. 根据任务类型选择合适的线程池
  2. 合理设置线程池参数(核心线程数、最大线程数、队列容量)
  3. 为线程池设置有意义的名称(通过自定义ThreadFactory)
  4. 监控线程池运行状态(如任务队列大小、活跃线程数等)
  5. 手动 new ThreadPoolExecutor 来创建线程池,防止资源耗尽导致 OOM 问题

4.8.线程池状态

线程池有5种状态:

  1. ​RUNNING​​:接受新任务并处理队列中的任务
  2. ​SHUTDOWN​​:不接受新任务,但处理队列中的任务
  3. ​STOP​​:不接受新任务,不处理队列中的任务,中断正在执行的任务
  4. ​TIDYING​​:所有任务已终止,workerCount为0,将执行terminated()方法
  5. ​TERMINATED​​:terminated()方法执行完成
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值