(一)概述
1、没有线程池的状态:
当我们使用一条线程的时候,先将线程对象创建出来,启动线程,在运行过程中,可能
能完成任务,也可能会在中途被任务内容中断掉,任务还没有完成。
即使是能够正常完成,线程对象就结束了,就变成了垃圾对象,需要被垃圾回收器回收
如果在系统中,大量的任务都是小任务,任务消耗时间较短、线程对象的创建和消亡耗
费的时间比较多,结果:大部分的时间都浪费在了线程对象的创建和死亡上。
如果任务本身破坏力比较大,可能会把线程对象结束掉,就无法继续完成任务。
2、有线程池的状态
在没有任务的时候,先把线程对象准备好,存储到一个容器中,一旦有任务来的时候,
就不需要创建对象,而是直接将对象获取出来执行任务
如果任务破坏力较小,任务可以直接完成,这个线程对象不会进入死亡状态,而是被容
器回收,继续活跃。
如果任务破坏力较大,任务会把线程搞死,线程池会继续提供下一个线程,继续完成这
个任务。
(二)使用
1、步骤:获取线程池对象;创建任务类对象;将任务类对象提交到线程池中
2、获取线程池对象:
工具类:Executors:生成线程池的工具类,根据需求生成指定大小的线程池
ExecutorService Executors.newSingleThreadPool():创建一个有单个线程的线程池
ExecutorService Executors.newFixedThreadPool(int nThreads):创建一个指定线
程数量的线程池
3、创建任务类对象:Runnable的实现类对象,用于定义任务内容
4、将任务类对象提交到线程池中ExecutorService:是一个接口,不需要手动创建这个接口的实现类对象,使用方法获取
到的就是这个接口的实现类对象,一定可以调用这个接口中的方法
submit(Runnable r):可以将一个任务类对象,提交到线程池中,如果有空闲的线程,
就可以马上运行这个任务,如果没有空闲线程,那么这个任务就需要等待。
shutDown():结束线程池,已经提交的全部保证完成,不准继续提交了
shutDownNow():结束线程池,已经开始运行的,保证完成;但是还没有运行的,已经提
交的,不给运行了,作为返回值范围;对于没有提交的,不准提交。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo15_UseThreadPool {
public static void main(String[] args) {
//线程池中线程的默认名称:pool-1-thread-1
/*
* 当提交的任务数量多于池中线程数,则先给所有线程安排任务去执行,哪一个线程先执行完了自己的任务,就立马执行排队的任务
*
* */
//1.获取线程池:ThreadPoolExecutor
ExecutorService es = Executors.newFixedThreadPool(2);
//2.将任务提交到线程池
//submit(Runnable task)
Runnable task1 = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("AAAAAAAAAAAAAAAAA" + Thread.currentThread().getName());
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Runnable task2 = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("@@@@@@@@@@@@@@@@@" + Thread.currentThread().getName());
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Runnable task3 = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("5555555555555555" + Thread.currentThread().getName());
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
es.submit(task1);
es.submit(task2);
es.submit(task3);
//关闭线程池,执行已经提交的任务和正在排队的任务,不接受新任务
es.shutdown();
//关闭线程池,尝试结束正在执行的任务,不执行正在排队的任务,不接受新任务
//es.shutdownNow();
}
}
线程池原理
(一)线程池部分体系
1、Executor:接口,定义了一个接收 Runnable 对象的方法executor(Runnable
command)
2、ExecutorService:比 Executor 使用更广泛的子接口
3、AbstractExecutorService:ExecutorService 抽象方法的实现类
4、ThreadPoolExecutor:线程池,可以通过调用 Executors 工具类的静态工厂
方法来创建线程池并返回一个 ExecutorService 的实现类对象
(二)ThreadPoolExecutor类概述
1、Executors工具类,是创建线程池的工具类
2、ExecutorService接口,是工具类返回线程池对象所实现的接口
3、ThreadPoolExecutor是ExecutorService接口的实现类,就是返回的线程池对
象所属类
4、ThreadPoolExecutor的构造方法:
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler) 用给定的初始参数创建新的 ThreadPoolExecutor。 |
ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) corePoolSize: 线程池的核心线程数。在创建了线程池后,默认情况下, 线程池中并没有任何线程,当有任务来之后,就会创建一个线程去执行任务, 当线程池中的线程数目达到 corePoolSize 后, 就会把到多出的任务放到队列当中 maximumPoolSize: 线程池允许创建的最大线程数。 如果队列满了,并且已创建的线程数小于最大线程数, 则线程池会再创建新的线程执行任务。 注意:如果使用了无界的任务队列这个参数就没什么效果。 keepAliveTime: 线程活动保持时间。线程池的工作线程空闲后,保持存活的时间。 如果任务很多,并且每个任务执行的时间比较短, 可以调大这个时间,提高线程的利用率。 unit: 参数 keepAliveTime 的时间单位,在TimeUnit枚举类中,有 7 种取值。 可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES), 毫秒(MILLISECONDS),微秒(MICROSECONDS),纳秒(NANOSECONDS) 用来指定keepAliveTime的单位,比如秒:TimeUnit.SECONDS workQueue: 任务队列。用于保存等待执行的任务的阻塞队列。 可以选择有界队列和无界队列 threadFactory: 线程工厂,用来创建线程。主要是为了给线程起名字, 默认工厂的线程名字:pool‐1‐thread‐1 Executors工具类中的defaultThreadFactory()方法可以获取到 用于创建新线程的默认线程工厂 handler: 拒绝策略,当线程池里线程被耗尽,且队列也满了的时候会调用 |
(三)阻塞队列和有界、无界队列
1、阻塞队列:是一个支持阻塞的插入和移除方法的队列
(1)支持阻塞的插入方法:当队列满时,队列会阻塞插入元素的线程,直
到队列不满
(2)支持阻塞的移除方法:当队列为空时,获取元素的线程会等待队列变
为非空
2、有界队列:具有固定大小的队列,可以进队的元素个数是有限的。
3、无界队列:是没有设置固定大小的队列。这些队列的特点是可以直接入列,
直到内存资源耗尽
4、相关类型即构造方法:
(1)ArrayBlockingQueue 基于数组实现的有界阻塞队列
(2)LinkedBlockingQueue 基于链表实现的队列,可以设定为有界,不设
定则无界
(3)SynchronousQueue 无界无缓冲的队列,内部容量为零,适用于元素数
量少的场景
(四)拒绝策略
1、ThreadPoolExecutor.AbortPolicy
(此项是默认策略)直接抛出异常RejectedExecutionException
2、ThreadPoolExecutor.CallerRunsPolicy
该任务被线程池拒绝,由调用 execute方法的线程执行该任务
3、ThreadPoolExecutor.DiscardOldestPolicy
抛弃队列最前面的任务,然后重新尝试执行任务
4、ThreadPoolExecutor.DiscardPolicy
丢弃任务,也不会抛出异常
(五)线程池执行流程
(六)有界队列和无界队列对线程池的影响
1、使用有界队列:如果有新的任务需要执行,如果线程池时机线程数量小于核
心线程数corePoolSize,则优先创建线程。如果大于核心线程数corePoolSize,
则会将任务加入队列。如果队列已满,则在总线程数量不大于最大线程数
maximumPoolSize的前提下,创建新的线程。如果线程数大于最大线程数
maximumPoolSize,则执行拒绝策略
2、使用无界队列:除了系统资源耗尽,否则无界队列不存在任务入队失败的情
况。当有任务到来,系统的线程数小于核心线程数corePoolSize时,新建线程执
行任务。如果后续仍然有新的任务加入,而没有空闲的线程,则任务直接入队等
待。如果任务处理慢,而任务添加快,则队列会快速增长,直到系统资源耗尽。