线程池的底层工作原理可以分为以下几个关键步骤:
- 线程池的创建: 在使用线程池之前,需要首先创建一个线程池。通常,线程池会根据配置参数(如核心线程数、最大线程数、队列类型等)来初始化线程池的基本属性。
- 任务提交: 当有任务需要执行时,将任务提交给线程池。任务可以是Runnable或Callable对象,表示需要在一个独立线程中执行的工作单元。
- 线程分配: 线程池内部维护了一组工作线程,这些线程会被动态分配来执行任务。线程池首先会尝试将任务分配给核心线程,如果核心线程数没有达到上限,就创建一个新的核心线程来执行任务。如果核心线程已满,任务会被放入任务队列中等待执行。
- 任务执行: 分配给线程的任务会被执行。每个工作线程会不断地从任务队列中获取任务并执行它们。一旦任务执行完成,线程可以选择等待新任务或被回收,具体取决于线程池的配置和实现方式。
- 线程回收: 线程池内的线程可能会被回收,这可以是根据一些策略,如闲置时间超过一定阈值或线程数超过最大线程数等。回收的线程会释放资源,如内存和CPU,以便在需要时重新使用。
- 任务完成和结果返回: 任务执行完成后,可以将执行结果返回给调用者。如果任务是通过Callable提交的,线程池会返回Future对象,通过该对象可以获取任务的执行结果。
- 异常处理: 线程池通常会处理任务执行过程中抛出的异常,可以将异常信息记录下来或采取适当的措施,以确保线程池的稳定性。
总的来说,线程池的底层工作原理是通过管理一组工作线程、任务队列和任务分配策略来实现任务的调度和执行。这种机制可以提高线程的重用性、提高程序性能,并有效地控制线程的生命周期,使得线程池成为多线程编程中的重要工具。
线程池的线程复用原理是指,将线程放入线程池中重复利用,而不是每执行一个任务就创建一个新线程。线程池会对线程进行封装,核心原理在于将线程的创建和管理与任务的执行分离。
线程池通过工作队列(WorkQueue)来存储待执行的任务,队列中可能有多个任务等待被执行。线程池中的线程数量是有限的,核心线程数通常是固定的,最大线程数可以设置,超过最大线程数后,任务会被拒绝。
当提交任务时,线程池首先会检查当前线程数是否小于核心线程数,如果是,则新建一个线程来执行任务;如果当前线程数已经达到核心线程数,但队列中没有正在执行的任务,则将任务放入队列中等待执行;如果队列已满,且线程池中的线程数量未达到最大线程数,则新建线程来执行任务;如果队列已满,且线程池中的线程数量达到最大线程数,则根据拒绝策略来处理无法执行的任务。
线程复用的关键是将任务的提交和线程的创建、管理、执行分离,通过线程池来统一管理和调度,减少了创建和销毁线程的开销,提高了系统的效率。同时,由于线程池的复用特性,可以有效控制并发度,避免大量线程的创建和销毁导致的系统负载过大。
// 创建一个线程池,根据实际需求设置线程数量 ExecutorService executorService = Executors.newFixedThreadPool(5); // 创建并发请求 List<Future<List<LyWebDeviceVO>>> futures = new ArrayList<>(); // for (int i = 0; i < 5; i++) { // 假设处理5个并发请求 Future<List<LyWebDeviceVO>> future = executorService.submit(() -> { // 调用服务处理前端请求,并返回处理结果 IPage<LyWebDeviceVO> pages = deviceService.selectDeviceMonitor(Condition.getPage(query), device); // 请根据实际需求调整参数 return pages.getRecords(); }); futures.add(future); // } List<LyWebDeviceVO> devices = new ArrayList<>(); // 等待所有请求完成 for (Future<List<LyWebDeviceVO>> future1 : futures) { try { List<LyWebDeviceVO> pageData = future1.get(); devices.addAll(pageData); // 将每个请求的结果添加到devices列表中 } catch (InterruptedException | ExecutionException e) { // 处理异常 e.printStackTrace(); } } // 关闭线程池 executorService.shutdown();