谷粒商城实战笔记-193~194-商城业务-多线程-线程池

一,193-商城业务-异步-异步复习

Java中使用线程的4种方式包括:

  • 继承Thread
  • 实现Runnable接口
  • 实现Callable接口结合FutureTask使用
  • 使用线程池。

1. 继承Thread

继承Thread类并重写run方法来定义线程执行的任务。这种方式简单直观,但存在一些限制。

代码示例

public class ThreadExample1 extends Thread {
    @Override
    public void run() {
        System.out.println("ThreadExample1 is running.");
    }
}

// 使用
ThreadExample1 thread = new ThreadExample1();
thread.start();

问题:继承Thread类会强制要求类继承自Thread,这限制了类的继承结构,因为Java不支持多重继承。

2. 实现Runnable接口

通过实现Runnable接口,可以避免继承Thread类的限制,因为Java允许一个类实现多个接口。

代码示例

public class RunnableExample1 implements Runnable {
    @Override
    public void run() {
        System.out.println("RunnableExample1 is running.");
    }
}

// 使用
RunnableExample1 runnable = new RunnableExample1();
new Thread(runnable).start();

问题:与继承Thread类类似,这种方式不能获取线程执行的返回值,也不能很好地控制资源。

3. 实现Callable接口结合FutureTask

Callable接口允许任务有返回值,并且可以通过FutureTask来管理线程的生命周期和结果。

代码示例

public class CallableExample1 implements Callable<Integer> {
    @Override
    public Integer call() {
        return 100;
    }
}

// 使用
FutureTask<Integer> futureTask = new FutureTask<>(new CallableExample1());
new Thread(futureTask).start();
try {
    Integer result = futureTask.get(); // 阻塞等待结果
    System.out.println("CallableExample1 returned: " + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

问题:尽管这种方式可以获取返回值,但创建和管理线程的开销仍然存在。

4. 使用线程池

线程池提供了一种高效的方式来管理线程,它可以复用线程,减少创建和销毁线程的开销,同时可以控制并发级别。

代码示例

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

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10); // 创建线程池
        Runnable task = () -> System.out.println("Task is running in thread pool.");
        executorService.submit(task); // 提交任务到线程池

        // 执行完毕后关闭线程池
        executorService.shutdown();
    }
}

优势

  • 资源控制:线程池可以控制并发线程的数量,有效管理资源。
  • 性能稳定:通过复用线程,减少了线程创建和销毁的开销,提高了性能。
  • 易于管理:线程池提供了统一的接口来提交和管理任务,简化了代码。

在实际开发中,推荐使用线程池来执行多线程任务,因为它提供了更好的资源管理和性能优势。

前三种方式虽然在某些简单场景下可以使用,但在需要高效资源管理和复杂任务调度的现代应用程序中,它们往往不够灵活和高效。

二,194-商城业务-异步-线程池详解

1,线程池七大参数

线程池(ThreadPoolExecutor)是Java并发编程中用于管理线程的一个核心组件。它允许你复用线程,从而减少因频繁创建和销毁线程而带来的开销。以下是线程池的七个主要参数,这些参数在创建线程池时可以进行配置:

  1. corePoolSize:线程池的基本大小,即在没有任务执行时,线程池中保留的线程数量。如果允许核心线程超时(通过设置allowCoreThreadTimeOuttrue),则即使线程池中的线程是空闲的,它们也会在一定时间后终止。

  2. maximumPoolSize:线程池中允许的最大线程数。如果任务队列满了,线程池会尝试创建新的线程,直到达到这个数目。

  3. keepAliveTime:当线程池中的线程数量超过corePoolSize时,多余的空闲线程能等待新任务的最长时间。如果在这个时间内没有新任务到达,这些线程将被终止。

  4. unitkeepAliveTime参数的时间单位,常见的时间单位有TimeUnit.SECONDSTimeUnit.MILLISECONDS等。

  5. workQueue:一个阻塞队列,用于存储等待执行的任务。这个队列只保存通过execute(Runnable)方法提交的Runnable任务。

  6. threadFactory:一个线程工厂,用于创建新线程。通过自定义线程工厂,你可以对线程的创建过程进行控制,例如设置线程的名称、优先级等。

  7. handler:当线程池饱和(即线程数量达到maximumPoolSize且任务队列已满)时,使用的饱和策略。饱和策略定义了如何处理无法立即执行的任务,常见的饱和策略有:

    • ThreadPoolExecutor.AbortPolicy:抛出RejectedExecutionException来拒绝新任务的处理。
    • ThreadPoolExecutor.CallerRunsPolicy:调用执行任务的线程(提交任务的线程)来运行当前任务。
    • ThreadPoolExecutor.DiscardPolicy:直接丢弃无法处理的任务。
    • ThreadPoolExecutor.DiscardOldestPolicy:丢弃任务队列中最老的任务,然后尝试再次提交当前任务。

这些参数共同定义了线程池的行为和性能。正确地配置这些参数对于优化应用程序的并发性能至关重要。例如,如果corePoolSizemaximumPoolSize设置得太高,可能会导致资源竞争和上下文切换的开销;如果设置得太低,则可能无法充分利用多核处理器的优势。同样,workQueue的选择也会影响线程池的吞吐量和延迟特性。

2,面试题

下面是一个面试题。
在这里插入图片描述

当有100个并发请求进入线程池时,线程池会按照以下步骤进行操作:

  1. 首先,线程池会尝试使用其核心线程来处理请求。由于核心线程数为7,所以线程池会立即启动7个线程来处理这7个请求。

  2. 如果还有更多请求进来,并且核心线程都在忙于处理其他请求,那么这些额外的请求会被放入队列中等待处理。由于队列容量为50,所以最多可以存放50个请求。

  3. 当队列也满了后,线程池会尝试创建新的非核心线程来处理请求。由于最大线程数为20,所以最多可以创建13个非核心线程。

  4. 如果此时仍然有请求进来,并且所有的线程都在忙于处理其他请求,那么线程池可能会选择拒绝这些请求。具体的行为取决于线程池的饱和策略。

3,Executors能创建的4中线程池

在实际开发过程中,一般用Executors创建线程池,Executors相当于一个工厂类,创建各种各样的线程池对象。

  1. FixedThreadPool:

    • 类型:固定大小的线程池。
    • 特点:线程池中的线程数量是固定的。当一个线程完成任务后,它会从队列中获取新的任务来执行。这种线程池能够保证程序的资源占用相对稳定。
    • 用途:适用于需要控制线程数量的场景,如后台任务处理、定时任务等。
  2. CachedThreadPool:

    • 类型:可缓存的线程池。
    • 特点:线程池的大小没有限制,可以根据需要动态地调整线程的数量。空闲的线程会在一段时间后自动终止。
    • 用途:适用于执行大量短小的任务,如网络I/O操作、计算密集型任务等。
  3. SingleThreadExecutor:

    • 类型:单线程的线程池。
    • 特点:线程池只有一个线程,确保所有任务按照指定顺序执行。
    • 用途:适用于需要保证任务顺序执行的场景,例如数据更新、文件写入等。
  4. ScheduledThreadPoolExecutor:

    • 类型:支持定时任务的线程池。
    • 特点:线程池可以调度任务以定期或延迟的方式执行。
    • 用途:适用于需要定期执行任务或延时执行任务的场景,如计划任务、周期性检查等。

下面是这四种线程池的具体实现方式:

FixedThreadPool

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(nThreads);

CachedThreadPool

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

SingleThreadExecutor

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

ScheduledThreadPoolExecutor

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(nThreads);

其中,nThreads 表示线程池中的线程数量。对于 newFixedThreadPoolnewScheduledThreadPool,这个值是必需的,而对于 newSingleThreadExecutornewCachedThreadPool,则不需要指定线程数量。

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小手追梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值