ThreadPoolExecutor的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
中间的数据验证我就删掉了,主要是这几个参数,corePoolSize
为线程池创建时的初始容量,类似于一个数组的大小,maximumPoolSize线程池允许的最大容量,workQueue工作队列,当线程池的空闲的线程数量超过corePoolSize
时设定多长时间销毁,threadFactory
用于创建线程,ThreadPoolExecutor
采用的是默认的方式Executors.defaultThreadFactory()
,handler
拒绝策略,就是如果线程总数大于maximumPoolSize时的拒绝策略
workQueue汇总
队列分为直接提交队列,有界任务队列,无界任务队列,任务有限队列
直接提交队列 SynchronousQueue
线上代码
package com.example.demo;
import java.util.concurrent.*;
public class ThreadPool {
private static ExecutorService threadService;
public static void main(String[] args) {
threadService = new ThreadPoolExecutor(1, 8, 1000, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 10; i++) {
threadService.execute(new ThreadTask());
}
}
}
class ThreadTask implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
创建一个多线程,线程池最大8个线程,然后加入直接提交队列,我们看一下结果
pool-1-thread-1
pool-1-thread-7
pool-1-thread-6
pool-1-thread-5
pool-1-thread-8
pool-1-thread-4
pool-1-thread-2
pool-1-thread-3
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.example.demo.ThreadTaskTest@27bc2616 rejected from java.util.concurrent.ThreadPoolExecutor@3941a79c[Running, pool size = 8, active threads = 0, queued tasks = 0, completed tasks = 8]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at com.example.demo.TestThreadPool.main(TestThreadPool.java:14)
可以看到我们执行了8个线程的时候,就执行了拒绝策略。而且最大线程ID就是8,所以可以看到他是直接自行,来一个执行一个,所以用这个队列的时候就要注意最大线程数了,太大的话,会吃爆内存,太小的话任务总是执行不完,正确估计才行
有界任务队列 ArrayBlockingQueue
还是直接看代码
package com.example.demo;
import java.util.concurrent.*;
public class ThreadPool {
private static ExecutorService threadService;
public static void main(String[] args) {
ArrayBlockingQueue<Runnable> runnables = new ArrayBlockingQueue<>(5);
threadService = new ThreadPoolExecutor(1,2, 1000, TimeUnit.SECONDS,
runnables ,Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 30; i++) {
threadService.execute(new ThreadTask(runnables));
}
}
}
class ThreadTask implements Runnable {
ArrayBlockingQueue<Runnable> runnables;
public ThreadTask(ArrayBlockingQueue<Runnable> runnables) {
this.runnables = runnables;
}
@Override
public void run() {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前队列线程数为 ->"+ runnables.size() + "线程名称为 ->" + Thread.currentThread().getName());
}
}
直接结果为
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.example.demo.ThreadTask@12edcd21 rejected from java.util.concurrent.ThreadPoolExecutor@34c45dca[Running, pool size = 2, active threads = 2, queued tasks = 5, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at com.example.demo.ThreadPool.main(ThreadPool.java:19)
当前队列线程数为 ->5线程名称为 ->pool-1-thread-1
当前队列线程数为 ->5线程名称为 ->pool-1-thread-2
当前队列线程数为 ->3线程名称为 ->pool-1-thread-2
当前队列线程数为 ->3线程名称为 ->pool-1-thread-1
当前队列线程数为 ->1线程名称为 ->pool-1-thread-2
当前队列线程数为 ->1线程名称为 ->pool-1-thread-1
当前队列线程数为 ->0线程名称为 ->pool-1-thread-2
可以看到一共有7个线程,这个有界队列是先创建corePoolSize大小的线程数量,然后多余的会放到队列中,如果队列满了,则继续创建线程,如果超过了maxPoolSize大小,则执行拒绝策略,。所以这个有界队列如果非常的大,还是会撑爆内存,这点要注意了。
###无界的任务队列 LinkedBlockingQueue
先看代码:
package com.example.demo;
import java.util.concurrent.*;
public class ThreadPool {
private static ExecutorService threadService;
public static void main(String[] args) {
LinkedBlockingQueue<Runnable> runnables = new LinkedBlockingQueue<Runnable>();
threadService = new ThreadPoolExecutor(1,2, 1000, TimeUnit.SECONDS,
runnables ,Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 30; i++) {
threadService.execute(new ThreadTask(runnables));
}
}
}
class ThreadTask implements Runnable {
LinkedBlockingQueue<Runnable> runnables;
public ThreadTask(LinkedBlockingQueue<Runnable> runnables) {
this.runnables = runnables;
}
@Override
public void run() {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前队列线程数为 ->"+ runnables.size() + "线程名称为 ->" + Thread.currentThread().getName());
}
}
执行结果
当前队列线程数为 ->9线程名称为 ->pool-1-thread-1
当前队列线程数为 ->8线程名称为 ->pool-1-thread-1
当前队列线程数为 ->7线程名称为 ->pool-1-thread-1
当前队列线程数为 ->6线程名称为 ->pool-1-thread-1
当前队列线程数为 ->5线程名称为 ->pool-1-thread-1
当前队列线程数为 ->4线程名称为 ->pool-1-thread-1
当前队列线程数为 ->3线程名称为 ->pool-1-thread-1
当前队列线程数为 ->2线程名称为 ->pool-1-thread-1
当前队列线程数为 ->1线程名称为 ->pool-1-thread-1
当前队列线程数为 ->0线程名称为 ->pool-1-thread-1
无解队列的最大线程数maxPoolSize其实是没有用的,我们可以看到他会一直网队列里扔,直到内存不够用,所以在我们不清楚具体要成熟多少的线程的时候,这个还是慎用
优先任务队列 PriorityBlockingQueue
先看代码:
package com.example.demo;
import java.util.Random;
import java.util.concurrent.*;
public class ThreadPoolPriority {
private static ExecutorService threadService;
public static void main(String[] args) {
PriorityBlockingQueue<Runnable> runnables = new PriorityBlockingQueue<Runnable>();
threadService = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.SECONDS,
runnables, Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 10; i++) {
threadService.execute(new ThreadTaskPriority(i, runnables));
}
}
}
class ThreadTaskPriority implements Runnable, Comparable<ThreadTaskPriority> {
private int priority;
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public ThreadTaskPriority(int priority) {
this.priority = priority;
}
@Override
public int compareTo(ThreadTaskPriority o) {
return this.priority > o.priority ? -1 : 1;
}
PriorityBlockingQueue<Runnable> runnables;
public ThreadTaskPriority(int priority, PriorityBlockingQueue<Runnable> runnables) {
this.priority = priority;
this.runnables = runnables;
}
@Override
public void run() {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("priority -> " + this.priority + " 当前队列线程数为 ->" + runnables.size() + "线程名称为 ->" + Thread.currentThread().getName());
}
}
结果为:
priority -> 0 当前队列线程数为 ->9线程名称为 ->pool-1-thread-1
priority -> 9 当前队列线程数为 ->8线程名称为 ->pool-1-thread-1
priority -> 8 当前队列线程数为 ->7线程名称为 ->pool-1-thread-1
priority -> 7 当前队列线程数为 ->6线程名称为 ->pool-1-thread-1
priority -> 6 当前队列线程数为 ->5线程名称为 ->pool-1-thread-1
priority -> 5 当前队列线程数为 ->4线程名称为 ->pool-1-thread-1
priority -> 4 当前队列线程数为 ->3线程名称为 ->pool-1-thread-1
priority -> 3 当前队列线程数为 ->2线程名称为 ->pool-1-thread-1
priority -> 2 当前队列线程数为 ->1线程名称为 ->pool-1-thread-1
priority -> 1 当前队列线程数为 ->0线程名称为 ->pool-1-thread-1
可以看出来,除了第一个任务i=0的时候是第一个执行的,剩下的线程都是根据priority大小倒序执行的。他也是一个无界队列,只不过是有一个特定的顺序,他必须要实现一个Comparable接口,来判断谁在前边执行,这个类似于我们的抢票了,登记越高,抢票越靠前。
拒绝策略
上边的四个队列我们用的拒绝策略都是java默认的拒绝策略,也就是达到条件以后会立刻失败,阻止程序的运行。接下来我们讲一下其他的策略和自定义策略。
- AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作
- CallerRunsPolicy策略:如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行
- DiscardOledestPolicy策略:该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的,马上要被执行的那个任务,并尝试再次提交
- DiscardPolicy策略:该策略会默默丢弃无法处理的任务,不予任何处理。当然使用此策略,业务场景中需允许任务的丢失;
当然上边的接口都实现了RejectedExecutionHandler接口,我们自定也的拒绝策略也是执行,自定义拒绝策略如下
package com.example.demo;
import java.util.concurrent.*;
public class ThreadPool {
private static ExecutorService threadService;
public static void main(String[] args) {
ArrayBlockingQueue<Runnable> runnables = new ArrayBlockingQueue<Runnable>(5);
threadService = new ThreadPoolExecutor(1,2, 1000, TimeUnit.SECONDS,
runnables ,Executors.defaultThreadFactory(), new RejectHandler());
for (int i = 0; i < 10; i++) {
threadService.execute(new ThreadTask(runnables));
}
}
}
class RejectHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(r.toString() + "拒绝请求");
}
}
class ThreadTask implements Runnable {
ArrayBlockingQueue<Runnable> runnables;
public ThreadTask(ArrayBlockingQueue<Runnable> runnables) {
this.runnables = runnables;
}
@Override
public void run() {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前队列线程数为 ->"+ runnables.size() + "线程名称为 ->" + Thread.currentThread().getName());
}
}
结果如下:
com.example.demo.ThreadTask@6a5fc7f7拒绝请求
com.example.demo.ThreadTask@3b6eb2ec拒绝请求
com.example.demo.ThreadTask@1e643faf拒绝请求
当前队列线程数为 ->5线程名称为 ->pool-1-thread-2
当前队列线程数为 ->4线程名称为 ->pool-1-thread-1
当前队列线程数为 ->3线程名称为 ->pool-1-thread-2
当前队列线程数为 ->3线程名称为 ->pool-1-thread-1
当前队列线程数为 ->1线程名称为 ->pool-1-thread-2
当前队列线程数为 ->1线程名称为 ->pool-1-thread-1
当前队列线程数为 ->0线程名称为 ->pool-1-thread-2
##自定义线程创建ThreadFactory
我们传进去的是runnable,然后线程池会为我们创建线程,默认使用的是Executors.defaultThreadFactory()
,代码如下:
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
当然我们也可以自定义,只要继承了ThreadFactory就可以,例如
class ThreadFactoryDemo implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
System.out.println("线程" + r.hashCode() + "创建");
return new Thread(r,"threadPool" + r.hashCode());
}
}
然后我们定义线程池的时候,把以前的Executors.defaultThreadFactory()
替换成new ThreadFactoryDemo()
就可以了。返回结果如下:
线程2093176254创建
线程1854731462创建
com.example.demo.ThreadTask@12edcd21拒绝请求
com.example.demo.ThreadTask@34c45dca拒绝请求
com.example.demo.ThreadTask@52cc8049拒绝请求
当前队列线程数为 ->5线程名称为 ->threadPool1854731462
当前队列线程数为 ->4线程名称为 ->threadPool2093176254
当前队列线程数为 ->3线程名称为 ->threadPool1854731462
当前队列线程数为 ->2线程名称为 ->threadPool2093176254
当前队列线程数为 ->1线程名称为 ->threadPool1854731462
当前队列线程数为 ->0线程名称为 ->threadPool2093176254
当前队列线程数为 ->0线程名称为 ->threadPool1854731462
##扩展ThreadPoolExecutor
hreadPoolExecutor扩展主要是围绕beforeExecute()、afterExecute()和terminated()三个接口实现的,
- beforeExecute:线程池中任务运行前执行
- afterExecute:线程池中任务运行完毕后执行
- terminated:线程池退出后执行
通过这三个方法,我们可以监控任务的执行情况,代码如下
package com.example.demo;
import java.util.concurrent.*;
public class ThreadPoolBefore {
private static ExecutorService threadService;
public static void main(String[] args) {
ArrayBlockingQueue<Runnable> runnables = new ArrayBlockingQueue<Runnable>(30);
threadService = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.SECONDS,
runnables, new ThreadFactoryDemo(), new ThreadPoolExecutor.AbortPolicy()) {
@Override
protected void beforeExecute(Thread t, Runnable r) {
System.out.println("准备执行:" + ((ThreadTaskBefore) r).getTaskName());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
System.out.println("执行结束:" + ((ThreadTaskBefore) r).getTaskName());
}
@Override
protected void terminated() {
System.out.println("线程池退出");
}
};
for (int i = 0; i < 10; i++) {
threadService.execute(new ThreadTaskBefore(runnables, "task" + i));
}
threadService.shutdown();
}
}
class ThreadTaskBefore implements Runnable {
ArrayBlockingQueue<Runnable> runnables;
public ThreadTaskBefore(ArrayBlockingQueue<Runnable> runnables) {
this.runnables = runnables;
}
private String taskName;
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public ThreadTaskBefore(ArrayBlockingQueue<Runnable> runnables, String taskName) {
this.runnables = runnables;
this.taskName = taskName;
}
@Override
public void run() {
System.out.println("当前队列线程数为 ->" + runnables.size() + "线程名称为 ->" + Thread.currentThread().getName());
}
}
执行效果如下:
线程1134712904创建
准备执行:task0
当前队列线程数为 ->9线程名称为 ->threadPool1134712904
执行结束:task0
准备执行:task1
当前队列线程数为 ->8线程名称为 ->threadPool1134712904
执行结束:task1
准备执行:task2
当前队列线程数为 ->7线程名称为 ->threadPool1134712904
执行结束:task2
准备执行:task3
当前队列线程数为 ->6线程名称为 ->threadPool1134712904
执行结束:task3
准备执行:task4
当前队列线程数为 ->5线程名称为 ->threadPool1134712904
执行结束:task4
准备执行:task5
当前队列线程数为 ->4线程名称为 ->threadPool1134712904
执行结束:task5
准备执行:task6
当前队列线程数为 ->3线程名称为 ->threadPool1134712904
执行结束:task6
准备执行:task7
当前队列线程数为 ->2线程名称为 ->threadPool1134712904
执行结束:task7
准备执行:task8
当前队列线程数为 ->1线程名称为 ->threadPool1134712904
执行结束:task8
准备执行:task9
当前队列线程数为 ->0线程名称为 ->threadPool1134712904
执行结束:task9
线程池退出
可以看到通过对beforeExecute()、afterExecute()和terminated()的实现,我们对线程池中线程的运行状态进行了监控,在其执行前后输出了相关打印信息。另外使用shutdown方法可以比较安全的关闭线程池, 当线程池调用该方法后,线程池中不再接受后续添加的任务。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。