赞
踩
(1)、ArrayBlockingQueue 基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
(2)、LinkedBlockingQuene基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
(3)、SynchronousQuene一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
(4)、PriorityBlockingQueue 具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
ArrayBlockingQueue和PriorityBlockingQueue一般很少使用
6、threadFactory:创建线程的工厂类,通常我们会自定义一个threadFactory设置线程的名称,这样我们就可以知道线程是由哪个工厂类创建的,可以快速定位。
7、handler:线程池执行拒绝策略,当线程数量达到maximumPoolSize大小,并且workQueue也已经塞满了任务的情况下,线程池会调用handler拒绝策略来处理请求。
public class HelloThreadPool {
static class Task implements Runnable {
private int i;
public Task(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " Task " + i);
try {
System.in.read();
//TimeUnit.MILLISECONDS.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "Task{" +
"i=" + i +
'}';
}
}
public static void main(String[] args) {
ThreadPoolExecutor tpe = new ThreadPoolExecutor(3, 5,
5, TimeUnit.SECONDS, //5 秒內沒有任务要处理,非核心线程释放掉
/**队列中最多能存入的任务数量
* 当所有的核心线程都在处理任务的时候,新进来的任务将放入队列中,如果队列中放满了,则新建额外的线程处理,总线程数量不能超过maximumPoolSize
*/
new ArrayBlockingQueue<Runnable>(4),
Executors.defaultThreadFactory(),// 默认的线程工厂,通过 new Thread 产生线程
/**四个拒绝策略:
* AbortPolicy:为线程池默认的拒绝策略,该策略直接抛异常处理。
* DiscardPolicy:直接抛弃不处理。
* DiscardOldestPolicy:丢弃队列中最老的任务。
* CallerRunsPolicy:将任务分配给当前执行 execute 的方法线程来处理,如这个调用方法中表示 main方法主线程。
*/
new ThreadPoolExecutor.CallerRunsPolicy());
//创建10个线程,核心线程限制性,然后放入等待队列,等待队列放满后再新建线程
for (int i = 0; i < 10; i++) {
tpe.execute(new Task(i));
}
System.out.println(tpe.getQueue());
tpe.execute(new Task(100));
System.out.println(tpe.getQueue());
tpe.shutdown();
}
线程在使用过程中新创建的值会放在threadLocalMap中,threadLocalMap是以threadlocal作为键、value作为值来存储的,对于普通线程当线程执行结束后线程被销毁、threadLocalMap也随之被清空;
但是对于线程池,线程执行完成后thread并不会被销毁,会将threadlocal置为空来保证同一个thread执行不同任务的数据安全,但是threadlocal被置为空后value还在,等到新的任务执行时也会产生threadLocalMap,不断的累计这些value值可能会导致内存溢出;
线程池防止内存溢出的解决办法是执行完一个任务后调用threadlocal的remove方法,清空这些value值;
从上面代码中的 tpe.execute(new Task(i)) 点进去:
可参考:【精选】线程池的设计与原理解析(三)之---addWorker()方法-CSDN博客
然后是创建一个任务线程,并将线程放入任务队列中,更新线程池中线程数量,如果添加到线程池失败则移除线程、线程数量减1
线程通过while从任务队列中获取任务执行,一直到没有可执行任务(线程池中没有任务或线程池执行了关闭方法)或执行任务出现异常跳出while循环,然后执行finally中的processWorkerExit方法,processWorkerExit 有两种逻辑异常退出和正常退出;
异常退出:线程执行过程中出现异常,更新可用线程数减1 ,在线程池中移除这个线程,然后在最后再添加一个线程到线程池中
正常退出:没有可执行任务,先移除这个线程,然后判断线程池中的线程数是否大于核心线程数,如果不满足则创建一个新的线程到线程池中
线程本身没有是否核心线程这个属性,只要线程没有可执行任务时就会调用processWorkerExit方法,然后就会被销毁,线程池中最后也只会保留核心数量的线程,而不管这些线程是刚开始的核心线程还是后面新建的线程;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。