线程池 (ThreadPoolExecutor)[Java]

多线程

线程的四种创建方式:

继承Thread类

实现Runnable接口

使用线程池创建线程

实现Callable接口

线程池

1使用线程池的好处

避免了频繁创建和销毁线程造成的资源和时间的浪费

缺点:第一次创建线程时需要消耗大量的资源和时间

2实现线程池的函数

工具类Executors(JDK5以后出现)

ExecutorService service = Executors.newFixedThreadPool(3);

public interface ExecutorService

extends java. util. concurrent. Executor

(1)固定线程池   newFixedThreadPool

public static java. util. concurrent. ExecutorService newFixedThreadPool(int nThreads )

该函数为ExecutorService下的静态函数

ExecutorService service = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
    service.submit(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    });
}

创建了一个有3个线程的固定线程池

提交5个任务

显示结果如下:

pool-1-thread-1

pool-1-thread-3

pool-1-thread-2

pool-1-thread-1

pool-1-thread-2

(2)单一线程池   newSingleThreadExecutor

(3)缓冲线程池   newCachedThreadExecutor

(4)时间调度线程池  newScheculedThreadExecutor

阿里巴巴规定以上创建线程的方式在Java开发中不允许使用,因此不做细致讲解

在实际开发中使用ThreadPoolExecutor 创建线程池

3.ThreadPoolExecutor

3.1 参数

具有7个参数

corePoolSize       核心线程数     

即使不工作也始终在线程池里的线程

maximumPoolSize  最大线程数

线程池中允许存在的最大线程,最大线程数=核心线程数+空闲线程数

keepAliveTime     空闲线程最大存活时间

空闲线程在被摧毁前等待新任务的最大存活时间

unit              空闲线程最大存活时间的单位

workQueue        任务队列

任务被执行前的等待队列,只有有任务被提交时才存在

threadFactory      线程工厂

创造线程的工厂,使用固定参数

handler           任务的拒绝执行策略

当提交的任务数目超出最大线程数和等待队列之和时,多余的任务被拒绝执行而执行的策略

包括四种执行策略:

AbortPolicy   中止

抛出RejectedExecutionException 异常,中止提交任务,一般为默认的参数,即在使用ThreadPoolExecutor创建线程时若输入5个或6个参数,默认为该执行策略(线程工厂参数也可不输入直接默认)

CallerRunsPolicy    调用者  运行

提交的任务由调用者直接执行,如主线程

DiscardOldestPolicy     丢弃  最旧的

将队列中等待时间最长的任务直接丢弃

DiscardPolicy

直接丢弃任务,不抛异常不执行操作

原版解释:(前面的我只是翻译了一下,个人英语水平有限)

corePoolSize – the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set

maximumPoolSize – the maximum number of threads to allow in the pool

keepAliveTime – when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating. unit – the time unit for the keepAliveTime argument

workQueue – the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.

threadFactory – the factory to use when the executor creates a new thread

handler – the handler to use when execution is blocked because the thread bounds and queue capacities are reached

3.2工作原理及示例

线程池任务执行

任务 --> 主线程 --已满--> 任务队列 --已满--> 空闲线程 --已满--> 执行拒绝策略

方法:

submit() 提交任务

shutdown()  销毁线程池,若线程池不销毁将一直存在

代码示例:

public static void main(String[] args) {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,
            3,
            2,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());
    for (int i = 0; i < 4 ; i++) {
        threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });
    }

threadPoolExecutor.shutdown();


}

结果:

pool-1-thread-1

pool-1-thread-2

pool-1-thread-1

pool-1-thread-3

这是没有异常的输出结果

将提交线程数改为2

结果:

pool-1-thread-1

pool-1-thread-1

发现都是由一个线程执行的

任务进入时等待队列未满,等待主线程执行的任务执行完,进入主线程执行

将提交线程数改为5(超过最大线程数+任务队列等待数)

结果:

pool-1-thread-1

pool-1-thread-3

pool-1-thread-2

pool-1-thread-1

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@6d6f6e28 rejected from java.util.concurrent.ThreadPoolExecutor@135fbaa4[Running, pool size = 3, active threads = 0, queued tasks = 0, completed tasks = 4]

at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)

at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)

at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)

at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)

at Demo.Demo.main(Demo.java:15)

只执行了4个任务,并抛出了异常

将执行策略改为CallerRunsPolicy,同时将提交任务数改为7

结果:

main

pool-1-thread-2

pool-1-thread-1

main

pool-1-thread-3

pool-1-thread-2

pool-1-thread-3

出现了线程复用执行的情况因此线程池执行了5个任务,由于拒绝执行策略主线程了2个任务

### Java 中 `ThreadPoolExecutor` 类的使用方法 #### 创建自定义线程池并执行任务 通过继承 `CustomThreadPool` 并实现其功能来展示如何创建和配置一个简单的线程池。下面的例子展示了如何初始化具有两个核心线程数目的线程池,并向其中提交五个任务。 ```java package com.howtodoinjava.threads; public class CustomThreadPoolExample { public static void main(String[] args) { // 初始化带有2个工作线程的核心线程池 CustomThreadPool customThreadPool = new CustomThreadPool(2); for (int i = 1; i <= 5; i++) { Task task = new Task("Task " + i); System.out.println("Created : " + task.getName()); // 提交任务给线程池去执行 customThreadPool.execute(task); } } } ``` 此代码片段说明了如何构建基本的任务调度框架[^1]。 #### 利用标准库中的工具简化线程池管理 为了更方便地管理和操作线程池,可以借助于 `Executors` 工具类提供的静态工厂方法。这些方法允许快速设置不同类型的预设线程池实例,而无需手动指定所有的参数细节。 ```java import java.util.concurrent.*; // 使用 Executors 来获取固定大小的线程池 ExecutorService executor = Executors.newFixedThreadPool(4); for(int i=0;i<10;i++){ Runnable worker = new WorkerThread("" + i); // 将任务交给线程池处理 executor.execute(worker); } // 关闭线程池以防止新任务被接受 executor.shutdown(); while (!executor.isTerminated()) {} System.out.println("Finished all threads"); ``` 这段程序利用了 `newFixedThreadPool()` 方法来获得拥有四个工作线程的标准线程池对象[^2]。 #### 定义具体的工作单元——事件处理器 当涉及到具体的业务逻辑时,则可以通过像 `EventHandler.handle()` 这样的函数来进行封装。每当有新的工作任务到来时,就可以调用该方法完成相应的数据处理流程。 ```java public class WorkEvent { private String workNo; // 构造器和其他成员省略... public String getWorkNo(){ return this.workNo; } } public class EventHandler { /** * 处理业务 */ public static void handle(WorkEvent workEvent){ System.out.println("正在处理,workNo=[" + workEvent.getWorkNo() + "]"); } } ``` 上述代码段描述了一个典型的业务场景下的任务接收与响应机制[^3]。 #### 结合实际案例理解概念 对于初学者来说,观看一些关于 `ThreadPoolExecutor` 应用的教学视频可能会更加直观易懂。例如,在 Bilibili 上有一个专门介绍这个主题的内容可以帮助加深印象[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值