池的概念,在数据库中存在连接池,在字符串中存在常量池等等池的概念都是一个道理。
为了「防止在使用的时候创建连接和销毁的时间损耗」,于是选择在服务开启的时候就创建一部分连接供后续使用,使用完以后放入池中,形成复用,而出现的池的概念.
线程池也是如此,在服务启动的时候通过spring
或者静态方法等初始化一个池子,之后的所有请求任务都直接添加到这个线程池中,线程池中的线程直接执行添加到线程池中的任务。
线程池存在一个核心线程数,就是在初始的时候,线程拥有的线程数量,当核心线程数不足以处理传入的任务的时候,就会将这个任务添加到阻塞队列中。
当阻塞队列饱和以后,线程池再次创建线程,直到最大线程数。
如果此时已经达到了最大线程数,还是无法处理任务,就会按照指定的拒绝策略拒绝任务。
当任务量不大的时候,线程会有空闲,当线程的空闲时间超过我们指定的最大空闲时间以后,就会被线程池销毁。
根据上面的讲解,我们很容易知道一个线程池需要什么参数。
首先,一个初始化线程数,即核心线程数coreSize
BlockingQueue
maxSize
RejectedExecutionHandler
keepAliveTime
和timeUnit
threadFactory
这就是一个线程池的七大参数,下面我们根据这个参数构建一个线程池
「本次线程池仅仅模拟了四个参数的使用,核心线程,最大线程,队列最大长度」
「任务类」
public class MyTask implements Runnable{
int id ;
public MyTask(int id) {
this.id = id;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name+"\t即将执行任务"+id);
try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(name+"\t完成了任务");
}
@Override
public String toString() {
return "MyTask{" +
"id=" + id +
'}';
}
}
「线程类」
/**
* 自定义一个线程
*/
public class MyThread extends Thread{
private String name ;
private List<Runnable> tasks ;
public MyThread(String name, List<Runnable> tasks) {
this.name = name;
this.tasks = tasks;
}
@Override
public void run() {
while(tasks.size()>0){
Runnable remove = tasks.remove(0);
remove.run();
}
}
}
「线程池类」
/**
* 自定义的线程池
*/
public class MyPool {
private int currentPoolSize ;
private int corePoolSize ;
private int maxPoolSize ;
private int workSize ;
private List<Runnable> tasks = new CopyOnWriteArrayList<>();
public MyPool(int corePoolSize, int maxPoolSize, int workSize) {
this.corePoolSize = corePoolSize;
this.maxPoolSize = maxPoolSize;
this.workSize = workSize;
}
public void sumbit(Runnable runnable){
//如果当前线程数小于核心线程数
if(tasks.size()>=workSize){
System.out.println("任务被丢弃");
}else{
tasks.add(runnable);
this.execTask(runnable);
}
}
public void execTask(Runnable runnable){
if(currentPoolSize <= corePoolSize){
new MyThread("线程"+currentPoolSize,tasks).start();
currentPoolSize++;
}else if(currentPoolSize < maxPoolSize){
new MyThread("非核心线程"+currentPoolSize,tasks).start();
currentPoolSize++;
}else{
System.out.println(runnable+"任务被缓存");
}
}
}
「执行器」
public class MyThreadPollTest {
public static void main(String[] args) {
MyPool myPool = new MyPool(2,4,20);
for (int i = 0; i < 30; i++) {
MyTask myTask = new MyTask(i);
myPool.sumbit(myTask);
}
}
}
「输出器」
yTask{id=4}任务被缓存
MyTask{id=5}任务被缓存
MyTask{id=6}任务被缓存
MyTask{id=7}任务被缓存
MyTask{id=8}任务被缓存
MyTask{id=9}任务被缓存
MyTask{id=10}任务被缓存
MyTask{id=11}任务被缓存
MyTask{id=12}任务被缓存
MyTask{id=13}任务被缓存
MyTask{id=14}任务被缓存
MyTask{id=15}任务被缓存
MyTask{id=16}任务被缓存
MyTask{id=17}任务被缓存
MyTask{id=18}任务被缓存
MyTask{id=19}任务被缓存
Thread-3 即将执行任务2
Thread-2 即将执行任务1
Thread-1 即将执行任务0
MyTask{id=20}任务被缓存
MyTask{id=21}任务被缓存
MyTask{id=22}任务被缓存
Thread-0 即将执行任务3
MyTask{id=23}任务被缓存
任务被丢弃
任务被丢弃
任务被丢弃
任务被丢弃
任务被丢弃
任务被丢弃
Thread-3 完成了任务
Thread-3 即将执行任务4
Thread-2 完成了任务
Thread-2 即将执行任务5
Thread-1 完成了任务
Thread-1 即将执行任务6
Thread-0 完成了任务
Thread-0 即将执行任务7
Thread-1 完成了任务
Thread-3 完成了任务
Thread-1 即将执行任务8
Thread-3 即将执行任务9
Thread-0 完成了任务
Thread-2 完成了任务
Thread-2 即将执行任务10
Thread-0 即将执行任务11
Thread-3 完成了任务
Thread-1 完成了任务
Thread-1 即将执行任务12
Thread-3 即将执行任务13
Thread-0 完成了任务
Thread-0 即将执行任务14
Thread-2 完成了任务
Thread-2 即将执行任务15
Thread-1 完成了任务
Thread-1 即将执行任务16
Thread-3 完成了任务
Thread-3 即将执行任务17
Thread-2 完成了任务
Thread-0 完成了任务
Thread-0 即将执行任务19
Thread-2 即将执行任务18
Thread-3 完成了任务
Thread-1 完成了任务
Thread-1 即将执行任务21
Thread-3 即将执行任务20
Thread-0 完成了任务
Thread-0 即将执行任务22
Thread-2 完成了任务
Thread-2 即将执行任务23
Thread-1 完成了任务
Thread-3 完成了任务
Thread-0 完成了任务
Thread-2 完成了任务
「线程体系图」
Java中提供了一个Executors
类获取一些特定的线程
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
这个线程池仅仅创建一个核心线程
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
可以执行定时任务的线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
scheduleAtFixedRate
间隔时间中包含了执行任务的时间scheduleWithFixedDelay
间隔时间中不包含执行任务的时间4.4 ThreadPoolExecutor
这是Java
提供的一个线程池,在开发中我们经常直接使用这种自定义的线程池
「构造方法:」
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
下面我们着重了解一下「阻塞队列」和「拒绝策略」
BlockingQueue
是一个接口,看一眼他的继承体系图
ArrayBlockingQueue
由数组构成的一个有界的阻塞队列LinkedBlockingQueue
由链表构成的一个有界的阻塞队列SynchronousQueue
不存储元素的队列,仅仅存放一个队列LinkedBlockingQueue
双端有界队列。PriorityBlockingQueue
支持优先级排序的有界队列DelayQueue
使用优先级队列实现的延迟无界队列LinkedTransferQueue
由链表结构组成的无界阻塞队列「三种执行方式」
add()和remove()
java.lang.IllegalStateException
offer
和poll
添加返回false;移除返回null
put
和take
element
和peek
线程池默认的拒绝策略是直接抛出异常
线程池存在四种拒绝策略
AbortPolicy
直接抛出异常,默认拒绝策略CallerRunsPolicy
将任务返回给调用者执行,即将任务返回给线程池的线程执行DiscardPolicy
直接拒绝DiscardOldestPolicy
尝试将阻塞队列中等待时间最长的出队,将最新的加入8020
原则,系统80%的时间内一秒都会产生100
个任务,一个任务执行时间为0.1
秒。一秒最多产生任务数量为1000
100*0.1=10
100*2=200
(最大任务数-任务队列长度)*单个任务执行时间 (1000-200)*0.1=800
上面线程池继承体系中,有一个ExecutorService
。这是Java
中内置的线程池接口
void shutdown() 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
List<Runnable> shutdownNow() 停止所有正在执行的任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
<T> Future<T> submit(Callable<T> task) 执行带返回值的任务,返回一个Future对象。
Future<?> submit(Runnable task) 执行 Runnable 任务,并返回一个表示该任务的 Future。
<T> Future<T> submit(Runnable task, T result) 执行 Runnable 任务,并返回一个表示该任务的 Future。