ScheduledThreadPoolExecutor类是ScheduleExecutorService接口的实现类。
这个接口是用来开启延时任务的线程池。
他会将这些任务都放入一个队列,先进先出。
scheduleAtFixedRate:
只有前一个任务执行完毕之后,后面的任务才能接着去执行;如果前一个任务执行的时间超过了周期时间,等前一个任务执行完毕之后,立即执行后面的任务。
如果前一个任务执行的时间少于周期时间,则需要再等待周期时间减任务执行时间才会执行后一个任务。
案例:
public class TestMain {
static int a=0;
public static void main(String[] args) {
//延迟周期任务线程池,corePoolSize表示线程池中的线程数量,这个数量包括空闲数量
ScheduledThreadPoolExecutor executor=new ScheduledThreadPoolExecutor(3);
/*
将任务防止到延迟线程池中,这里表示一开始延迟4秒钟,后面每10秒执行一次这个线程
*/
executor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("测试:"+(TestMain.a++));
try {
Thread.sleep(4000);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 4, 10, TimeUnit.MILLISECONDS);
}
}
如果正常情况下,当前任务执行完之后需要再等待6秒之后才会执行下一个任务。
输出结果:
测试:0
测试:1
测试:2
测试:3
。。。。。。
如果是scheduleWithFixedDelay 方法的话,虽然结果是一样的。但是当前任务执行完之后需要再等待10秒之后才会执行下一个任务。
scheduleWithFixedDelay与scheduleAtFixedRate的区别就是一个在运行任务的时候就计时,一个任务运行完之后才计时。
主要是用来存错已经提交的任务,但是该任务没未能马上分配到线程去执行的任务,该api是一个接口。
常用的实现类有:
1、LinkedBlockingQueue
该api理论上是无界限,除非系统资源耗尽,否则可以一直讲任务存入该队列中。
采用先进先出策略。
2、ArrayBlockingQueue
有界限的任务队列,必须传入一个代表该队列最大容量的参数。
采用先进先出策略。
3、SynchronousQueue
直接提交的队列,提交的任务将会被直接分配线程执行。如果线程数达到最大数量,则会执行拒绝策略。
4、PriorityBlockingQueue
可指定任务的优先顺序,优先级越高其任务越优先分配到线程去执行。
创建一个只有一个线程的线程池,其任务队列使用的是LinkedBlockingQueue无界限队列。
因为这里的队列是采用的FIFO算法,而且线程池的线程数量又只有一个,因此这里的请求是有顺序的。
任务是按添加的顺序执行的,但是他不能保证每个线程执行的结果是有序的。
//创建单个线程的线程池
/* ExecutorService executor= Executors.newSingleThreadExecutor();
for(int a=0;a<10;a++){
PrintTask task=new PrintTask(a);
executor.execute(task);
}
executor.shutdown();
*/
一开始没有固定的线程数,当有任务直接创建一个线程,如果有空闲的线程直接使用,若没有则继续创建。
//创建一个无界限的线程池
/* ExecutorService executorService=Executors.newCachedThreadPool();
for(int a=0;a<100;a++){
PrintTask task=new PrintTask(a);
executorService.execute(task);
}
executorService.shutdown();*/
创建一个固定数量的线程池,其任务队列使用的是LinkedBlockingQueue无界限队列。
//固定数量的线程池
/* ExecutorService executorService=Executors.newFixedThreadPool(10);
for(int a=0;a<20;a++){
PrintTask task=new PrintTask(a);
//当任务超过线程池的线程池数量之后,会把任务放入线程池的任务队列种。
executorService.execute(task);
}*/
ExecutorService executorService = Executors.newScheduledThreadPool(30);
for (int a = 0; a < 10; a++) {
PrintTask task = new PrintTask(a);
executorService.execute(task);
}
executorService.shutdown();
PrintTask类:
public class PrintTask implements Runnable {
private int a;
public PrintTask(int a) {
this.a = a;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "----" + a);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程结束----" + a);
}
}
该线程池和其他四个都不同,他创建线程池传递的参数是parallelism ,表示并发数量,创建的任务队列的个数是 parallelism+1 个,线程也是paralleism+1个,当各自的线程维护的队列的任务完成之后,就会从其他的线程队里偷取任务来执行。
这也是newWorkStealingPool和以上四种线程数很大不同的区别,而且其他四种线程只维护一个任务队列。
ExecutorService注重于线程池的管理。
submit():
提交一个可运行的任务到线程池中,并返回一个该任务的Future(任务执行结果的一个包装对象),Future提供的get方法,将在成功完成后返回给定的结果,get()方法可以配置等待时间。
Future对象也可以取消这个任务的执行。
execute():
将该任务添加到线程池中,在某个时间执行。和submit不同的是,execute方法没有返回值。
shutdown():
方法允许先前提交的任务在线程池终止之前执行,但不会接受新的任务了,若任务全部执行完毕,线程池终止。
这个方法会先让线程池的状态预设置成停止,但只有在调用该方法之前的任务执行完之后才会停止掉线程池。
shutdownNow():
阻止任务队列中任务的启动并且尝试停止正在执行的任务,一旦终止,线程池中将没有任务执行,并且不能提交新的任务。
说笼统些,就是立马停止线程池,对于正在执行的任务会尝试去中断,它是通过interrupt()方法去中断任务的,对于一些没有sleep,wait,Condition的任务来说是没有办法中断的,只能等待执行完毕之后才停止线程池。
isisTerminated():
只有当线程池关闭了,该方法才会返回true。
例如:
public class TestExecutors {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = new ThreadPoolExecutor(1, 3, 5,
TimeUnit.SECONDS, new LinkedBlockingDeque<>(4), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
System.out.println("inited");
for (int i = 0; i < 20; i++) {
int finalI = i;
executorService.execute(() -> {
try {
TimeUnit.SECONDS.sleep(10);
System.out.println(finalI);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
System.out.println(executorService.isTerminated());
executorService.shutdown();
System.out.println(executorService.isTerminated());
System.out.println(1111111);
}
}
从结果上看到,调用了shutdown() 方法,并不是真正的将线程池关闭了。但如果换成shutdownNow()就会直接关闭线程池了。
该接口和ExecutorService十分类似,不过CompletionService更注重的是任务队列的执行情况,实际上CompletionService是Executor和BlockQueue的结合。
事实上CompletionService就是用来管理线程管理线程池提交的那些任务完成后的结果的一个api。
task():
等待当前执行的任务的完成情况,返回Future对象。
任务的创建有两种创建方式:Runnale,Callable
1、Runnable:这种方式一般是用来执行无返回值的。
2、Callable:这种任务的创建方式可以返回执行结果。
可以看成一个线程就是一个任务,然后让Executors工厂提供一个线程池去管理这些任务。ExecutorService主要
Executors是操作线程池的工具类,Executor是所有线程池的终极父接口,ExecutorService比较注重任务的运行和管理。
ForkJoinTask:可以将一个任务分解成多个,然后执行完之后再将任务的结果合并。
ForkJoinTask它的实现类有RecursiveTask。
注意:
线程池技术,内部使用了一个任务队列的方式。当任务超出了最大线程数量之后,则会将任务阻塞在任务队列中,然后等待现在正在执行任务的线程,执行完当前任务之后,才会去从任务队列中取任务执行,知道所有任务执行完毕。