概念:解决了频繁的创建和销毁线程,在线程池中总有几个活跃的线程, 当我们需要使用时,拿走池子里的空闲线程即可, 用完后还给池子!
线程池工厂类:Excutors
我们可以根据工厂类得到不同工作特性的线程池
1. newFixedThreadPool(): 该方法可以得到固定线程数据的线程池
2. newSingleThreadPool(): 该方法返回只有一个线程的线程池
3. newCachedThreadPool(): 该方法返回一个根据实际情况调成线程数量的线程池
4. newSingleThreadScheduleExcutor(): 该方法返回一个ScheduleExcutorService对象。线程数量为1, 其在接口ExecutorService接口上扩展了任务调度
5. newScheduleThreadPool(): 该方法返回一个ScheduleExcutorService对象, 可以指定线程数量
计划任务: ScheduleExcutorService
核心API:
A. schedule(Runnable r, long delay, TimeUnit time) 会在给定时间执行一次任务调度
B. scheduleAtFixedRate(Runnable r, long inititalDelay , long period, TimeUnit time): 周期性调度==> inititalDelay + n*period 比如说任务执行时间1秒钟, 周期时间2秒钟, 那么这个任务会每隔俩秒执行一次。 但是执行任务大于了周期时间,比如5秒,那么将会每隔5秒立马调度一次。inititalDelay只会执行一次延迟
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1) ;
pool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Date date = new Date() ;
System.out.println(date.getSeconds());
}
}, 0, 2, TimeUnit.SECONDS) ;
}
B. scheduleWithFixedRate(Runnable r, long inititalDelay , long period, TimeUnit time): 周期性调度==> n*inititalDelay + n*period 每次周期调度时间都是延迟加周期。
刨根究底:核心线程池内部实现 ThreadPoolExcetor
我们上述讲的工厂类产生的几种线程池都是通过ThreadPoolExcetor包装得到的!首先看一下它的内部构造:
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
nThreads, // 核心线程数
maxSize, // 最大线程数量
keepAliveTime, // 超过核心线程数的最大存活时间
TimeUnit.MILLISECONDS, // 时间单位
new LinkedBlockingQueue<Runnable>(), // 任务队列,存储尚未执行的任务
Executors.defaultThreadFactory(), // 线程工厂, 创建线程的, 默认即可
// 拒绝策略
new AbortPolicy()
);
}
任务队列:BlockingQueue 用于存储Runnable对象
1. 直接提交的队列: SysnchronousQueue===>newCachedThreadPool使用的这个队列, 核心线程数为0, maxSize无穷大, 每次使用空闲线程执行任务, 如果没有加入队列, 队列则直接提交,会迫使线程池增加新的线程。
2. 有界任务队列: ArrayBlockingQueue. 如果当前任务执行, 实际线程数小于核心线程数,则创建新线程, 一旦超过了核心线程即加入等待队列,这时任务队列满了,核心线程中还没有空闲线程, 即在不大于MaxSize线程总数的情况下, 开辟新线程。 换言之, 除非系统非常繁忙, 否则确保线程数维持在核心线程数量上。
3. 无界任务队列: 通过LinkedBlockingQueue实现, 与有界相比, 除非系统资源耗尽, 否则不会出现任务入队失败的情况。 线程在达到核心线程数时, 不在增加。
4. 优先级队列: PriorityBlockingQueue, 带有优先级的无界任务队列
使用自定义线程池, 根据应用情况, 选择合适的缓冲并发队列。
拒绝策略:
AbortPolicy策略: 直接抛出异常, 阻止系统正常运行
CallerRunsPolicy: 只有线程池未关闭, 即使用调用者线程,执行该任务
DisCardOlderstPolicy: 丢弃最老的请求
DiscardPolicy: 默默丢弃无法处理的请求
我们也可以通过RejectedExcetionHandler实现自定义策略
在线程池使用中, 很多时候可能出现一些异常, 但是我们看不见完整的异常信息,1. 这时候我们只需要把submit() 改为 execute()
2.
public class MyEexcutors extends ThreadPoolExecutor {
public static void main(String[] args) {
ThreadPoolExecutor eexcutors =
new MyEexcutors(0, Integer.MAX_VALUE, 0L, TimeUnit.SECONDS, new SynchronousQueue<>()) ;
for(int i=0; i<5; i++) {
eexcutors.execute(new DivTask(100, i));
}
}
public MyEexcutors(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
public void execute(Runnable task) {
super.execute(wrap(task, clientTrace(), Thread.currentThread().getName()));
}
private Runnable wrap(Runnable task, final Exception clientTrace, String name) {
return new Runnable() {
@Override
public void run() {
try {
task.run();
}catch (Exception e) {
clientTrace.printStackTrace();
throw e ;
}
}
};
}
private Exception clientTrace() {
return new Exception("Client stack trace");
}
}
public class DivTask implements Runnable {
int a ;
int b ;
public DivTask(int a, int b) {
this.a = a ;
this.b = b ;
}
@Override
public void run() {
double re = a/b ;
System.out.println(re);
}
}