Java核心基础六:多线程、线程和线程池

一、进程和线程

1. 进程(Process)
  • 定义:进程是操作系统资源分配的基本单位,是程序的一次执行过程。每个进程都有独立的内存空间、文件描述符、环境变量等。

  • 特点

    • 独立性:进程之间相互隔离,一个进程崩溃不会影响其他进程。

    • 资源开销大:创建和切换进程需要较大的系统开销。

    • 通信复杂:进程间通信(IPC)需要通过管道、消息队列、共享内存等方式。

2. 线程(Thread)
  • 定义:线程是CPU调度的基本单位,是进程中的一个执行流。一个进程可以包含多个线程,所有线程共享进程的内存空间和资源。

  • 特点

    • 轻量级:创建和切换线程的开销比进程小。

    • 共享资源:线程可以直接访问进程的全局变量和堆内存。

    • 通信简单:线程间通信可以通过共享内存实现,但需要同步机制(如锁)来避免竞争。


3.进程与线程的区别
特性进程线程
定义操作系统资源分配的基本单位CPU调度的基本单位,进程中的执行流
内存空间独立的内存空间共享进程的内存空间
资源开销创建和切换开销大创建和切换开销小
通信方式进程间通信(IPC)复杂(如管道、消息队列)线程间通信简单(共享内存)
独立性进程间相互隔离,一个进程崩溃不影响其他进程线程共享资源,一个线程崩溃可能影响整个进程
并发性进程间并发线程间并发
创建与销毁创建和销毁速度慢创建和销毁速度快
适用场景需要高隔离性的任务(如浏览器多标签页)需要高并发、低开销的任务(如Web服务器)

总结

  • 进程是资源分配的基本单位,适合需要高隔离性的任务。

  • 线程是CPU调度的基本单位,适合需要高并发、低开销的任务。

  • 理解进程和线程的区别是掌握多线程编程和操作系统原理的基础。


二、线程生命周期

Java线程的生命周期分为6种状态(根据Thread.State枚举):

  1. NEW(新建):线程被创建但未调用start()

  2. RUNNABLE(可运行):调用start()后,线程在JVM中等待CPU调度或正在运行。

  3. BLOCKED(阻塞):等待获取监视器锁(如进入synchronized代码块)。

  4. WAITING(无限期等待):调用Object.wait()Thread.join()LockSupport.park(),需其他线程唤醒。

  5. TIMED_WAITING(限期等待):带超时的等待,如Thread.sleep(ms)Object.wait(timeout)

  6. TERMINATED(终止):线程执行完毕或异常退出。

状态转换示例

Thread thread = new Thread(() -> {
    try {
        Thread.sleep(1000); // 进入TIMED_WAITING
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
thread.start(); // 进入RUNNABLE

面试重点

  • BLOCKEDWAITING的区别?
    BLOCKED是等待锁,WAITING是主动调用等待方法。


三、线程池原理(ThreadPoolExecutor)

“线程池”是一种用于管理和复用线程的技术,它通过预先创建一组线程并维护一个任务队列,来高效地处理并发任务。线程池的核心思想是避免频繁创建和销毁线程,从而减少系统开销,提高性能。


线程池的核心概念

1. 为什么需要线程池?
  • 线程创建和销毁开销大:频繁创建和销毁线程会消耗大量系统资源。

  • 资源管理复杂:手动管理线程容易导致资源耗尽或线程泄漏。

  • 任务调度优化:线程池可以合理分配任务,避免线程过多导致上下文切换频繁。

2.  线程池的组成部分
  • 核心线程(Core Threads):线程池中始终存活的线程,即使空闲也不会被销毁。

  • 任务队列(Work Queue):用于存放待执行的任务。

  • 非核心线程(Non-Core Threads):当任务队列满时,线程池会创建额外的线程来处理任务。

  • 拒绝策略(Rejected Execution Handler):当线程池和任务队列都满时,如何处理新提交的任务。


线程池的工作原理

  1. 提交任务:当有新任务提交时,线程池会优先使用核心线程处理。

  2. 任务队列:如果核心线程都在忙,任务会被放入任务队列等待。

  3. 创建非核心线程:如果任务队列已满,线程池会创建非核心线程处理任务。

  4. 拒绝策略:如果线程池和任务队列都满,新任务会触发拒绝策略。


Java中的线程池实现

Java通过java.util.concurrent包提供了线程池的实现,核心类是ThreadPoolExecutor

1. 创建线程池
ExecutorService executor = new ThreadPoolExecutor(
    2, // 核心线程数
    5, // 最大线程数
    60, TimeUnit.SECONDS, // 非核心线程空闲存活时间
    new LinkedBlockingQueue<>(10), // 任务队列
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
2. 核心参数
  1. corePoolSize(核心线程数):即使空闲也不会被回收的线程数。

  2. maximumPoolSize(最大线程数):线程池允许的最大线程数。

  3. keepAliveTime(空闲线程存活时间):非核心线程空闲超过此时间会被回收。

  4. workQueue(任务队列):用于保存待执行任务的阻塞队列(如LinkedBlockingQueue)。

  5. RejectedExecutionHandler(拒绝策略):当线程池和队列都满时的处理策略。

3. 拒绝策略

  • AbortPolicy(默认):抛出RejectedExecutionException

  • CallerRunsPolicy:由提交任务的线程直接执行。

  • DiscardPolicy:静默丢弃新任务。

  • DiscardOldestPolicy:丢弃队列中最旧的任务,重试提交。


线程池的使用场景

  1. Web服务器:处理大量并发请求。

  2. 数据处理:批量处理数据任务。

  3. 定时任务:通过ScheduledThreadPoolExecutor执行定时任务。


线程池的优点

  1. 降低资源消耗:复用线程,减少创建和销毁的开销。

  2. 提高响应速度:任务到达时可以直接执行,无需等待线程创建。

  3. 提高线程的可管理性:统一管理线程的生命周期和任务分配。


面试常见问题

  1. 线程池的核心参数有哪些?
    核心线程数、最大线程数、任务队列、拒绝策略、非核心线程存活时间。

  2. 如何选择合适的线程池参数?
    根据任务类型(CPU密集型、IO密集型)和系统资源调整。

  3. 线程池的拒绝策略有哪些?
    AbortPolicyCallerRunsPolicyDiscardPolicyDiscardOldestPolicy

  4. 线程池的任务队列满了会怎样?
    如果任务队列满且线程数达到最大值,会触发拒绝策略。


总结

线程池是并发编程中的重要工具,通过合理配置线程池参数和拒绝策略,可以显著提高系统的性能和稳定性。理解线程池的工作原理和使用场景,是掌握多线程编程的关键。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值