文章目录
- 线程池
- 前言
- 1.线程池的创建方式
- 1.1 newCachedThreadPool方式
- 1.2 newFixedThreadPool
- 1.3 newScheduledThreadPool
- 1.4 newSingleExecutor
- 2. ThreadPoolExecutor的介绍
- 2.1 构造方法的核心参数介绍
- 2.2 线程池的拒绝策略
- 3.线程池的工作原理
线程池
前言
Java中有许多池,字符串常量池、数据库连接池、线程池。对于线程池来说,我们提前将线程创建好放进池子里,当我们要使用时直接从池子中拿,而不是再从系统申请,我们用完之后,就还给池子而不是直接销毁。从池子里拿线程和还线程的过程中,我们都是纯用户态的操作,而不是直接创建或销毁线程涉及到操作系统的内核态。==这样在频繁创建销毁的情况下,就能节省开销并且提高效率。==以下我们将要介绍Java标准库中现有线程池的使用。
1.线程池的创建方式
Executors是一个工厂类,我们通过它的方法可以创建4种不同风格的线程池,这些方法都是最终通过配置ThreadPoolExecutor类的不同参数实现。Executors本质是对ThreadPoolExecutor类的封装。
ExecutorService表示线程池的一个实例,我们常常用它来接收创建的线程池对象,它的submit方法能够向线程池提交若干个任务。
1.1 newCachedThreadPool方式
该方法是创建一个线程数目动态增长的线程池。如果线程池的长度超过处理需要,就可以灵活回收空闲的线程,如果没有空闲的线程回收,就创建一个新的线程。
举例使用:
package threading;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author zq
* 四种创建线程池的方式
*/
public class TD19 {
public static void main(String[] args) {
//创建可缓存(动态增长)的线程池
ExecutorService pool = Executors.newCachedThreadPool();
//循环加入任务
int i = 0;
while (i<5){
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
i++;
}
}
}
执行结果:
1.2 newFixedThreadPool
这种方法是创建一个固定数目的线程池,当任务超过当前线程池的数量时就会一直等待,直到执行。
**举例使用:**创建10大小的线程池,加入五个任务
public static void main(String[] args) {
//创建一个固定数目的线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
//创建任务
for (int i = 0; i < 5; i++) {
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello F");
}
});
}
}
执行结果
1.3 newScheduledThreadPool
该方法是可以创建一个定长,支持定时的任务的线程池
举例使用: 我们此时需要用ScheduledExecutorService类型接收我们创建的需要实现定时执行的线程池。
public static void main(String[] args) {
//创建一个固定数量,支持定时任务,周期任务执行的线程池
//延迟两秒后执行线程池所有任务
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
for (int i = 0; i < 5; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello S");
}
};
pool.schedule(runnable,2, TimeUnit.SECONDS);
}
}
1.4 newSingleExecutor
该方法创建的是一个单线程的线程池,保证所有任务按顺序执行。
举例使用
public static void main(String[] args) {
//创建单线程线程池
ExecutorService pool = Executors.newSingleThreadExecutor();
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello1" );
}
});
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello2");
}
});
}
执行结果
2. ThreadPoolExecutor的介绍
我们在实际开发中通常通过new ThreadPoolExecutor的对象创建线程池,这种方法可以更灵活的创建线程池。
2.1 构造方法的核心参数介绍
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize:线程池中的核心线程数,即使空闲也不会死亡。
- maximumPoolSize:最大线程数,线程池可容纳的最大数量。
- keepAliveTime:闲置超时时间,当活跃线程数大于核心线程数时,超过该时间将会回收非核心的线程。
- unit:存活时间的单位。
- workQueue:存放任务的阻塞队列,我们可以手动指定一个队列。
- handler:超过线程范围和队列容量的线程的任务处理程序==(拒绝策略)==。
- threadFactory:工厂模式,创建线程多的辅助类。
2.2 线程池的拒绝策略
在核心参数中我们可以指定一种线程的拒绝策略。在多线程中我们主要有四种拒绝策略:
如图所示
- 第一种AbortPolicy(),满了直接抛异常。
- 第二种CallerRunsPolicy(),添加的线程自行负责执行这个任务。(在主线程中运行,不进入线程池)
- 第三种DiscardOldestPolicy(),丢弃最老的任务的未处理任务,然后加入新增的任务在等待队列。
- 第四种DiscardPolicy(),丢弃最新的任务,就是丢弃这个新增的任务,不抛异常。
3.线程池的工作原理
我们用流程图来描述线程池的工作原理,如图所示: