一.线程池的概念
创建Java线程需要给线程分配堆栈内存以及初始化内存,还需要进行系统调用,频繁地创建和销毁线程会大大降低系统的运行效率,采用线程池来管理线程有以下好处:
- 提升性能:线程池能独立负责线程的创建、维护和分配
- 线程管理:每个Java线程池会保持一些基本的线程统计信息,对线程进行有效管理
二.代码部分
1.处理任务类:向线程池中提交任务
定义两个链表:分别向其中添加任务和线程
package ThreadPool;import java.util.ArrayList; //处理任务的线程 public class WorkThread implements Runnable{public ArrayList<Runnable> runnableArrayList;public WorkThread(ArrayList<Runnable> runnableArrayList){this.runnableArrayList=runnableArrayList;}@Overridepublic void run() {Runnable work=null;while (true){synchronized (runnableArrayList){//上锁while(runnableArrayList.isEmpty()){//当list为空 即没有任务时 线程等待try {System.out.println(Thread.currentThread().getName()+"线程等待...");runnableArrayList.wait();} catch (Exception e) {throw new RuntimeException(e);}}//list不为空,取出第一个任务work=runnableArrayList.remove(0);}work.run();//任务运行 注意用run()}} }
2.线程池类
package ThreadPool;import java.util.ArrayList;public class ThreadPool{public ArrayList<WorkThread> workThreadArrayList;//任务listpublic ArrayList<Runnable> runnableArrayList;//线程listpublic int workSize;//任务数量public ThreadPool(int core,int size){workThreadArrayList=new ArrayList<>(core);//核心线程数corerunnableArrayList=new ArrayList<>(size);//任务数量sizeworkSize=size;for(int i=0;i<core;i++){WorkThread wt=new WorkThread(runnableArrayList);//新建一个任务new Thread(wt).start();//任务运行workThreadArrayList.add(wt);//将任务加入任务list}}//添加任务的方法public void add(Runnable runnable){//参数是Runnablesynchronized (runnableArrayList){if(runnableArrayList.size()<workSize){runnableArrayList.add(runnable);runnableArrayList.notify();}else{System.out.println("队列已满...");}}} }
3.处理任务的对象
package ThreadPool;public class ThreadDemo {public static void main(String[] args) { //main中定义一个线程池参数为核心线程数和线程池尺寸ThreadPool threadPool=new ThreadPool(5,20);for(int i=0;i<25;i++){threadPool.add(new Work(i));//调用添加方法}} } //待处理任务对象 class Work implements Runnable{int count=0;//任务编号public Work(int count){this.count=count;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"正在处理任务"+count);try {Thread.sleep(3000);} catch (Exception e) {throw new RuntimeException(e);}} }
三.线程池的任务调度流程
如果当前工作线程数量小于核心线程数量,执行器总是优先创建一个任务线程,而不是从线程队列中获取一个空闲线程
如果线程池中总的任务数量大于核心线程池数量,新接收的任务将被加入阻塞队列中,一直到阻塞队列已满。
当完成一个任务的执行时,执行器总是优先从阻塞队列中获取下一个任务,并开始执行,一直到阻塞队列为空
在核心线程池数量已经用完、阻塞队列也已经满了的场景下,如果线程池接收到新的任务,将会为新任务创建一个线程(非核心线程),并且立即开始执行新任务
在核心线程都用完、阻塞队列已满的情况下,一直会创建新线程去执行新任务,直到池内的线程总数超出maximumPoolSize。如果线程池的线程总数超过maximumPoolSize,线程池就会拒绝接收任务,当新任务过来时,会为新任务执行拒绝策略
注意点:
核心和最大线程数量、BlockingQueue队列等参数如果配置得不合理,可能会造成异步任务得不到预期的并发执行,造成严重的排队等待现象
线程池的调度器创建线程的一条重要的规则是:在corePoolSize已满之后,还需要等阻塞队列已满,才会去创建新的线程
example: 设置核心线程数量为1,阻塞队列为100,有5个任务待执行(假设极端情况下任务一直执行不接受),则只有1个任务可以被执行,其他4个任务在阻塞队列中,而不是创建新线程进行处理(阻塞队列未满)
四.任务阻塞队列
特点:在一个线程从一个空的阻塞队列中获取元素时线程会被阻塞,直到阻塞队列中有了元素;当队列中有元素后,被阻塞的线程会自动被唤醒
常见的几种阻塞队列的实现:
ArrayBlockingQueue:是一个数组实现的有界阻塞队列(有界队列),队列中的元素按FIFO排序,ArrayBlockingQueue在创建时必须设置大小
LinkedBlockingQueue:是一个基于链表实现的阻塞队列,按FIFO排序任务,可以设置容量(有界队列),不设置容量则默认使用Integer.Max_VALUE作为容量(无界队列)
PriorityBlockingQueue:是具有优先级的无界队列