Java 线程池

一、传统线程

        默认的线程创建不可复用,执行完会销毁,重复创建和销毁会浪费资源及时间

        Runnable task1 = () -> System.out.println("run1..." + Thread.currentThread().getId());
        Runnable task2 = () -> System.out.println("run2..." + Thread.currentThread().getId());
        Runnable task3 = () -> System.out.println("run3..." + Thread.currentThread().getId());
        Thread thread1 = new Thread(task1);
        Thread thread2 = new Thread(task2);
        Thread thread3 = new Thread(task3);
        thread1.start();
        thread2.start();
        thread3.start();

二、线程池

        是一种多线程处理形式,处理过程中将任务添加到队列,然后分发给线程执行。线程池的主要作用包括:

  • 重用线程:线程池会在内部维护一组可重用的线程,避免了频繁地创建和销毁线程的开销,提高了线程的利用率。
  • 控制并发度:线程池可以限制并发执行的线程数量,防止系统过载。通过调整线程池的大小,可以控制并发度,避免资源消耗过大。
  • 提供线程管理和监控:线程池提供了一些管理和监控机制,例如线程池的创建、销毁、线程状态的监控等,方便开发人员进行线程的管理和调试。
  • 提供任务队列:线程池通常会使用任务队列来存储待执行的任务,这样可以实现任务的缓冲和调度。

三、线程池的关键组成部分:

        线程池管理器(ThreadPoolExecutor):负责创建、管理和控制线程池。它负责线程的创建、销毁和管理,以及线程池的状态监控和调度任务。
        工作队列(BlockingQueue):用于存储待执行的任务。当线程池中的线程都在执行任务时,新的任务会被放入工作队列中等待执行。
        线程池线程(Worker Thread):实际执行任务的线程。线程池中会维护一组线程,这些线程可以被重复使用,从而避免了频繁创建和销毁线程的开销。

//构造方法
public ThreadPoolExecutor(int corePoolSize, //核心线程数量
                              int maximumPoolSize,//     最大线程数
                              long keepAliveTime, //       最大空闲时间
                              TimeUnit unit,         //        时间单位
                              BlockingQueue<Runnable> workQueue,   //   任务队列
                              ThreadFactory threadFactory,    // 线程工厂
                              RejectedExecutionHandler handler  //  饱和处理机制
	) 
{ ... }

四、ExecutorService

        ExecutorService接口是java内置的线程池接口,通过学习接口中的方法,可以快速的掌握java内置线程池的基本使用


常用方法:

  • void shutdown()   启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 
  • List<Runnable> shutdownNow() 停止所有正在执行的任务,暂停处理正在等待的任务,并返回等待执行的任务列表。 
  • <T> Future<T> submit(Callable<T> task)  执行带返回值的任务,返回一个Future对象。 
  •  Future<?> submit(Runnable task)  执行 Runnable 任务,并返回一个表示该任务的 Future。 
  •  <T> Future<T> submit(Runnable task, T result)  执行 Runnable 任务,并返回一个表示该任务的 Future。 
@SpringBootTest
class DemoApplicationTests {

    @Test
    void contextLoads() {
        ExecutorService executorService = new ThreadPoolExecutor(
                3,
                5,
                3,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());
        
        Future<Integer> future = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                // 模拟耗时操作
                Thread.sleep(1000);
                return 123;
            }
        });
        try {
            Integer result = future.get(); // 阻塞等待结果
            System.out.println("Task result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown(); // 关闭线程池
        }
    }
}

ThreadPoolExecutor 提供了四种内置的拒绝策略:

  • AbortPolicy:默认策略,直接抛出 RejectedExecutionException 异常。
  • CallerRunsPolicy:将任务交给调用者线程(即提交任务的线程)来执行。
  • DiscardPolicy:默默地丢弃无法处理的任务。
  • DiscardOldestPolicy:丢弃队列中最老的一个任务,然后尝试重新提交当前任务。

五、execute和submit

        在Java的ExecutorService中,execute和submit是两个用于提交任务到线程池的方法,但它们之间有一些重要的区别:

1. 返回类型不同

        execute(Runnable command):
        该方法接受一个Runnable类型的参数。它没有返回值,即返回类型为void。

        主要用于那些不需要返回结果的任务。

        submit:
        submit方法有多个重载版本,可以接受Runnable或Callable类型的参数。

        当提交一个Runnable任务时,submit方法会返回一个Future<?>对象,表示任务的未来结果。虽然Runnable本身不返回任何结果,但Future对象可以用来检查任务是否完成或取消任务。

        当提交一个Callable任务时,submit方法会返回一个Future<V>对象,其中V是Callable任务的返回类型。通过Future.get()方法可以获取任务的返回值。


2. 使用场景不同

        execute:
        适用于那些只需要执行某个操作而不需要关心执行结果的场景。
        例如,后台日志记录、定时任务等。

        submit:
        适用于需要获取任务执行结果或处理任务异常的场景。
        例如,计算密集型任务、数据处理任务等。


六、多线程模拟实例

public static void main(String[] args) {
    int numberOfTasks = 30; // 定义任务数量
    ExecutorService executorService = new ThreadPoolExecutor(
            3, // 核心线程数
            5, // 最大线程数
            3, // 空闲线程存活时间
            TimeUnit.SECONDS, // 时间单位
            new ArrayBlockingQueue<>(3), // 工作队列
            Executors.defaultThreadFactory(), // 线程工厂
            new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略

    List<Future<Integer>> futures = new ArrayList<>();

    for (int i = 0; i < numberOfTasks; i++) {
        final int taskId = i;
        Future<Integer> future = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println(Thread.currentThread().getName()+"Task " + taskId + " started");
                Thread.sleep(1000 + (taskId * 100)); // 模拟不同耗时的操作
                System.out.println(Thread.currentThread().getName()+"Task " + taskId + " completed");
                return taskId * 10; // 返回任务ID乘以10作为结果
            }
        });
        futures.add(future);
    }

    // 收集所有任务的结果
    for (Future<Integer> future : futures) {
        try {
            Integer result = future.get(); // 阻塞等待结果
            System.out.println("Task result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    executorService.shutdown(); // 关闭线程池
}

  1.执行循序

        循环 i = 0~29,合计30次循环,其中submit是并发操作,循环最后会依次将future加入到futures集合中,进入到处理结果的循环中,此处都是顺序执行,会按照futures中存储的顺序依次打印结果,如果get不到结果,会阻塞等待。

  2.并发顺序:

         核心线程数为 3,最大线程数为 5,工作队列容量为 3。
        当前 3 个核心线程都在忙时,新的任务会被放入工作队列(加上核心线程合计5个)。
        当核心线程和工作队列都满时,新的任务会由调用者线程(即主线程)执行,直至有空闲线程。  


七、注册线程池Bean

1.注册线程池单例 Bean

@Configuration
@Data
public class ExecutorConfig {
    @Bean(name="taskPool")
    public Executor asyncTaskPool(){
        //此类由Spring提供,org.springframework.scheduling.concurrent包下,是线程池的封装类
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //线程池中线程的名字前缀
        executor.setThreadNamePrefix("async-task-");
        //线程池核心线程数量
        executor.setCorePoolSize(4);
        //线程池最大线程数量
        executor.setMaxPoolSize(10);
        //线程池空闲线程存活时间,单位秒
        executor.setKeepAliveSeconds(100);
        //线程池拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //线程池中核心线程是否允许超时,默认为false
        executor.setAllowCoreThreadTimeOut(true);
        //线程池中的超时处理时间,单位秒
        executor.setAwaitTerminationSeconds(1200);
        //初始化线程池,不可以少,否者会抛出 线程池没有初始化
        executor.initialize();
        return executor;
    }
}

2.使用线程池

@Autowired
@Qualifier("taskPool")
private ExecutorService executorService;


CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
    //......    
},executorService);


 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值