【1】常见接口和实现类
① 什么是线程池
首先可以联想一下数据库连接池,Redis中的pool。
线程池提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。
② 常见的体系结构
常见的线程池体系结构:
java.util.concurrent.Executor : 负责线程的使用与调度的根接口
|--ExecutorService 子接口: 线程池的主要接口
|--ThreadPoolExecutor 线程池的实现类
|--ScheduledExecutorService 子接口:负责线程的调度
|--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService
如下图所示:
线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行任务集时使用的线程)的方法。每个ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。
③ 线程池的创建
为了便于跨大量上下文使用,此类提供了很多可调整的参数和扩展钩子(hook)。但是,强烈建议程序员使用较为方便的Executors 工厂方法:
- Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)
- Executors.newFixedThreadPool(int)(固定大小线程池)
- Executors.newSingleThreadExecutor()(单个后台线程)
- Executors.newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。
需要注意的是,它们均为大多数使用场景预定义了设置,也就是说通常情况下可以直接使用无需额外配置。
如下所示:
ScheduledExecutorService scheduledExecutorService =
Executors.newScheduledThreadPool(5);
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService1 = Executors.newFixedThreadPool(5);
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
【2】线程池使用实例
线程池提交任务的三个方法:
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
实例代码如下:
package com.jane.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* Created by Janus on 2018/9/28.
*/
public class TestThreadPool {
public static void main(String[] args) throws Exception {
//1.创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
//参数为Runnable
ThreadPoolRunnable threadPoolRunnable = new ThreadPoolRunnable();
// 2.为线程池中的线程分配任务
// for (int i = 0; i <10 ; i++) {
// pool.submit(threadPoolRunnable);
// }
//参数为Callable
ThreadPoolCallable threadPoolCallable = new ThreadPoolCallable();
List<Future<Integer>> futures = new ArrayList<>();
for (int i = 0; i <10 ; i++) {
Future<Integer> future = pool.submit(threadPoolCallable);
futures.add(future);
}
for (Future<Integer> future : futures) {
System.out.println(future.get());
}
//3.关闭线程池
pool.shutdown();
}
}
class ThreadPoolRunnable implements Runnable{
private int i = 0;
@Override
public void run() {
while(i <= 100){
System.out.println(Thread.currentThread().getName() + " : " + i++);
}
}
}
class ThreadPoolCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum =0;
for(int i=1;i<=100;i++){
sum+=i;
}
System.out.println(Thread.currentThread().getName() + " : " +sum);
return sum;
}
}
【3】线程池调度实例
ScheduledExecutorService使用实例代码如下:
package com.jane.controller;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.*;
/**
* Created by Janus on 2018/9/29.
*/
public class TestScheduledThreadPool {
public static void main(String[] args){
System.out.println("项目启动 : "+new Date());
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
// 延迟两秒执行ScheduledThread
ScheduledThread scheduledThread = new ScheduledThread();
scheduledExecutorService.schedule(scheduledThread,2, TimeUnit.SECONDS);
//项目启动时延迟一秒,然后以2秒为周期执行
// ScheduledRunnable scheduledRunnable = new ScheduledRunnable();
// scheduledExecutorService.scheduleAtFixedRate(scheduledRunnable,1,2,TimeUnit.SECONDS);
// 上一次结束后 延迟一秒,然后以2秒为周期执行
// scheduledExecutorService.scheduleWithFixedDelay(scheduledRunnable,1,2,TimeUnit.SECONDS);
}
}
class ScheduledThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int i = new Random().nextInt(100);
System.out.println(Thread.currentThread().getName()+" : "+i+" , "+new Date());
return i;
}
}
class ScheduledRunnable implements Runnable{
@Override
public void run() {
int i = (int) (Math.random()*1000);
System.out.println(Thread.currentThread().getName()+" : "+i+" , "+new Date());
}
}
测试一,单独执行schedule:
ScheduledThread scheduledThread = new ScheduledThread();
scheduledExecutorService.schedule(scheduledThread,2, TimeUnit.SECONDS);
效果如下:
项目启动 : Sat Sep 29 10:37:16 CST 2018
pool-1-thread-1 : 69 , Sat Sep 29 10:37:19 CST 2018
在主程序启动2秒后执行,效果正常。
测试二,单独执行scheduleAtFixedRate:
ScheduledRunnable scheduledRunnable = new ScheduledRunnable();
scheduledExecutorService.scheduleAtFixedRate(scheduledRunnable,1,2,TimeUnit.SECONDS);
效果如下:
项目启动 : Sat Sep 29 10:38:51 CST 2018
pool-1-thread-1 : 475 , Sat Sep 29 10:38:52 CST 2018
pool-1-thread-1 : 444 , Sat Sep 29 10:38:54 CST 2018
pool-1-thread-2 : 37 , Sat Sep 29 10:38:56 CST 2018
pool-1-thread-1 : 619 , Sat Sep 29 10:38:58 CST 2018
pool-1-thread-3 : 296 , Sat Sep 29 10:39:00 CST 2018
pool-1-thread-3 : 304 , Sat Sep 29 10:39:02 CST 2018
主程序启动,一秒延迟后,以2秒为间隔周期执行。
测试三,单独执行scheduleWithFixedDelay:
ScheduledRunnable scheduledRunnable = new ScheduledRunnable();
scheduledExecutorService.scheduleWithFixedDelay(scheduledRunnable,1,2,TimeUnit.SECONDS);
效果如下:
项目启动 : Sat Sep 29 10:42:41 CST 2018
pool-1-thread-1 : 473 , Sat Sep 29 10:42:42 CST 2018
pool-1-thread-1 : 667 , Sat Sep 29 10:42:44 CST 2018
pool-1-thread-2 : 788 , Sat Sep 29 10:42:46 CST 2018
pool-1-thread-1 : 836 , Sat Sep 29 10:42:48 CST 2018
pool-1-thread-3 : 457 , Sat Sep 29 10:42:50 CST 2018
pool-1-thread-3 : 923 , Sat Sep 29 10:42:52 CST 2018
pool-1-thread-3 : 120 , Sat Sep 29 10:42:54 CST 2018
看起来和测试二一样?
测试四,添加ScheduledRunnable2 ,run中sleep一秒:
添加一个类ScheduledRunnable2 如下:
class ScheduledRunnable2 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int i = (int) (Math.random()*1000);
System.out.println(Thread.currentThread().getName()+" : "+i+" , "+new Date());
}
}
测试代码如下:
ScheduledRunnable2 scheduledRunnable2 = new ScheduledRunnable2();
scheduledExecutorService.scheduleWithFixedDelay(scheduledRunnable2,1,2,TimeUnit.SECONDS);
效果如下:
项目启动 : Sat Sep 29 10:51:15 CST 2018
pool-1-thread-1 : 521 , Sat Sep 29 10:51:17 CST 2018
pool-1-thread-1 : 334 , Sat Sep 29 10:51:20 CST 2018
pool-1-thread-2 : 388 , Sat Sep 29 10:51:23 CST 2018
pool-1-thread-1 : 528 , Sat Sep 29 10:51:26 CST 2018
pool-1-thread-3 : 862 , Sat Sep 29 10:51:29 CST 2018
pool-1-thread-3 : 312 , Sat Sep 29 10:51:32 CST 2018
pool-1-thread-3 : 534 , Sat Sep 29 10:51:35 CST 2018
pool-1-thread-3 : 452 , Sat Sep 29 10:51:38 CST 2018
启动后,延迟一秒,再sleep一秒,然后打印第一次调用;之后等待上一次结束后以2秒周期间隔再执行。
注意,后面间隔都是三秒,充分说明了第二次调用是在第一次调用结束后(因此每次执行都会sleep 1 秒)!
测试五 ,使用scheduleAtFixedRate调用scheduledRunnable2:
测试代码如下:
ScheduledRunnable2 scheduledRunnable2 = new ScheduledRunnable2();
scheduledExecutorService.scheduleAtFixedRate(scheduledRunnable2,1,2,TimeUnit.SECONDS);
效果如下:
项目启动 : Sat Sep 29 10:57:27 CST 2018
pool-1-thread-1 : 500 , Sat Sep 29 10:57:29 CST 2018
pool-1-thread-1 : 991 , Sat Sep 29 10:57:31 CST 2018
pool-1-thread-2 : 1 , Sat Sep 29 10:57:33 CST 2018
pool-1-thread-1 : 241 , Sat Sep 29 10:57:35 CST 2018
pool-1-thread-3 : 92 , Sat Sep 29 10:57:37 CST 2018
pool-1-thread-3 : 517 , Sat Sep 29 10:57:39 CST 2018
项目启动后,延迟一秒,又sleep 1 秒,然后打印第一次调用;之后以2秒间隔周期进行线程调用!!
由此可见FixedDelay与FixedRate区别:
- FixedDelay 上一次结束后,等待X秒,执行下一次;
- FixedRate 两次调用间隔X秒。
参考博文:SpringBoot中定时任务使用。