关于线程池

JDK中提供的线程池ThreadPoolExecutorScheduledExecutorService

 

 

 

 

创建线程池的几种方法

线程池的常用创建方式主要有两种,通过Executors工厂方法创建(内部调用了ThreadPoolExecutor)和直接new ThreadPoolExecutor方法创建。

 

 

 

线程池的工作流程

线程池中刚开始没有线程,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。

当线程数达到 corePoolSize 并没有线程空闲,这时再加入任务,新加的任务会被加入workQueue 队列排队,直到有空闲的线程。

如果队列选择了有界队列,那么任务超过了队列大小时,会创建 maximumPoolSize - corePoolSize 数目的线程来救急。

如果线程到达 maximumPoolSize 仍然有新任务这时会执行拒绝策略。

当高峰过去后,超过corePoolSize 的救急线程如果一段时间没有任务做,需要自行关闭节省资源,这个时间由构造方法里的keepAliveTime 和 unit 参数来控制

 

 

ThreadPoolExecutor的构造方法

corePoolSize 核心线程数目 核心线程不会被销毁。

maximumPoolSize 最大线程数目

keepAliveTime 生存时间 - 针对救急线程

unit 时间单位 - 针对救急线程

workQueue 阻塞队列

handler 拒绝策略

threadFactory 线程工厂 - 可以为线程创建时起个好名字

阻塞队列:

1ArrayBlockingQueue:基于数组的先进先出队列,有界。此队列创建时必须指定大小;

2LinkedBlockingQueue:基于链表的先进先出队列,无界。默认为Integer.MAX_VALUE

3synchronousQueue它没有容量,是一个不存储元素的阻塞队列,只有当有空余线程的时候才会把任务放入队列并立刻被执行,否则不会放进队列。。

4DelayedworkQueue无界延迟队列,用于任务调度线程池

 

实现原理

使用通知模式实现,生产者往满的队列里添加元素时会阻塞,当消费者消费后,会通知生产者当前队列可用。当往队列里插入一个元素,如果队列不可用,阻塞生产者主要通过 LockSupport 的 park 方法实现,不同操作系统中实现方式不同,在 Linux 下使用的是系统方法 pthread_cond_wait 实现。

 

拒绝策略 jdk 提供了 4 种实现

异常策略AbortPolicy 直接抛出异常,这是默认策略

返回策略CallerRunsPolicy 让提交该任务的线程运行任务

丢弃策略DiscardPolicy 直接丢弃任务

取代策略DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之

 

 

Executors

工具类、线程池的工厂类,用于创建并返回不同类型的线程池,内部调用了ThreadPoolExecutor和ScheduledExecutorService的构造方法。

 

Executors.newFixedThreadPool(n); 创建一个可重用的固定线程数的线程池。特点是最大线程数就是核心线程数,没有救急线程,所以等待时间为0;阻塞队列LinkedBlockingQueue是无界的,可以放任意数量的任务。适用于任务量已知,相对耗时的任务

 

Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池。特点是核心线程数是 0, 最大线程数是 Integer.MAX_VALUE,救急线程的空闲生存时间是 60s,意味着全部都是救急线程(60s 后可以回收), 救急线程可以无限创建;阻塞队列采用了 SynchronousQueue ,它没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,向队列中添加元素时必须要等到有消费者消费元素后才会把元素放入队列并立刻被消费者取走,否则不会放进队列。适合任务数比较密集,但每个任务执行时间较短的情况

 

Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池特点是多个任务排队执行,核心线程数和最大线程数固定为 1,任务数多于 1 时,会放入LinkedBlockingQueue无界队列排队。任务执行完毕,这唯一的线程也不会被释放。

单线程线程池和不用线程池自己new的区别:

自己创建一个单线程串行执行任务,如果任务出现异常而终止那么下面的任务代码不会再继续执行,但线程池会新建一个线程,保证阻塞队列后面的任务正常工作

单线程线程池和newFixedThreadPool(1)的区别:

单线程线程池的线程数量固定为1,无法被修改。因为单线程线程池实际返回的是FinalizableDelegatedExecutorService而不是ThreadPoolExecutor,只是把ThreadPoolExecutor当做参数,所以只能把它当做返回类型ExecutorService;而newFixedThreadPool实际返回的是ThreadPoolExecutor,不仅可把它当做返回类型ExecutorService,还可以将它强转成ThreadPoolExecutor从而调用方法修改核心线程等。

 

Executors.newScheduledThreadPool(n):创建一个任务调度线程池线程池,用法和上面的一样,但是它用于在给定延迟后运行命令或者定期地执行。特点是返回ScheduledExecutorService,内部调用的还是ThreadPoolExecutor的构造方法,采用DelayedworkQueue无界延迟队列,最大线程数无限但是用不上。

 

 

线程池Max最大个数

如果是cpu密集型

cpu 核数 + 1

如果是IO密集型

线程数 = 核数 * 期望 CPU利用率 * 总时间(CPU计算时间+等待时间) / CPU 计算时间

 

 

execute()方法和submit()方法的区别

execute()方法只能执行Runnable 类型的任务,没有返回值。submit()方法可以执行Runnable和 Callable类型的任务,有返回值,返回Future类型。

 

 

使用线程池的好处:

1、提高响应速度(减少了创建新线程的时间)

2、降低资源消耗(重复利用线程池中线程,不需要每次都创建)

3、便于线程管理

 

 

 

 

 

步骤:

1、创建指定数量的线程池,使用Executors工具类。

ExecutorService executorService = Executors.newFixedThreadPool(int);

ScheduledExecutorService sES = Executors.newScheduledThreadPool(int)

▲Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

Executors.newFixedThreadPool(n); 创建一个可重用的固定线程数的线程池

Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池

Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池

Executors.newScheduledThreadPool(n):创建一个任务调度线程池线程池,它可安排在给定延迟后运行命令或者定期地执行

2、创建Runnable接口或Callable接口的实现类,重写run()或call()方法。

3、多次调用execute/submit/schedule这种方法实现线程池执行排队任务的功能

executorService.execute(Runnable 接口实现类对象);该方法没有返回值

executorService.submit(Runnable 接口实现类对象);该方法有返回值,返回值类型为Future

executorService.submit(Callable 接口实现类对象);该方法有返回值,返回值类型为Future(其实就是FutureTask,因为Future只有这一个实现类),作用是使用返回对象的get()方法得到call方法的返回值

executorService.invokeAll(Callable 接口实现类对象的集合);该方法有返回值,返回类型为元素是Future的List集合

executorService.invokeAny(Callable 接口实现类对象的集合);该方法有返回值,返回类型为最先执行完的任务的返回值,其他任务取消

sES.schedule(Runnable 接口实现类对象,1, TimeUnit.SECONDS):1秒后执行一次性任务。

sES.scheduleAtFixedRate(Runnable 接口实现类对象,23 TimeUnit.SECONDS):2秒后开始执行定时任务,不管是否执行完,每3秒执行下一次。

sES.scheduleWithFixedDelay(Runnable 接口实现类对象,23 TimeUnit.SECONDS):2秒后开始执行定时任务,任务执行完间隔3秒执行下一次。

 

4、关闭线程池(这个不要忘!!!!)

executorService.shutdown():等待正在执行的任务工作队列里的任务先完成,然后再关闭。

executorService.shutdownNow():会使用interrupt中断正在执行的任务返回等待队列中的任务,关闭线程池。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
关于 Java 线程池导致 MySQL 的 Bug,可能是由于线程池中的线程数量过多,导致 MySQL 连接池被耗尽,从而出现连接超时或者连接泄露的情况。这种情况下,可以通过优化线程池的配置,增加 MySQL 连接池的大小,或者使用连接池管理工具进行监控和管理,来避免这种情况的发生。 至于关于线程池的问题,可以具体分为以下几个方面: 1. 线程池的大小:线程池的大小需要根据实际的业务场景来进行设置,如果线程池的大小过小,可能会导致任务无法及时处理,而过大则会占用过多的系统资源,影响系统的性能表现。 2. 线程池的类型:线程池的类型包括 FixedThreadPool、CachedThreadPool、ScheduledThreadPool 等,不同类型的线程池适用于不同的场景,需要根据实际的业务需求进行选择。 3. 线程池的拒绝策略:当线程池中的任务数量超过线程池的最大容量时,需要采取一定的拒绝策略,如 AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy 等,需要根据业务场景和系统性能要求进行选择。 4. 线程池的生命周期管理:线程池的生命周期包括创建、启动、运行、停止等多个阶段,在使用线程池时需要对其进行合理的生命周期管理,以确保线程池的稳定运行和性能表现。 总之,线程池是一个非常重要的并发编程工具,需要在实践中不断学习和积累经验,以提高系统的性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值