##线程池 不需要如何创建线程,只需要关心业务逻辑。线程池统一创建,管理,销毁。 线程可以重用,避免创建销毁的资源开销。
##阿里规范关于线程池的几点要求
2. 【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
正例:自定义线程工厂,并且根据外部特征进行分组,比如机房信息。
public class UserThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger nextId = new AtomicInteger(1);
// 定义线程组名称,在 jstack 问题排查时,非常有帮助
UserThreadFactory(String whatFeaturOfGroup) {
namePrefix = "From UserThreadFactory‘s " + whatFeaturOfGroup + "-Worker-";
}
@Override
public Thread newThread(Runnable task) {
String name = namePrefix + nextId.getAndIncrement();
Thread thread = new Thread(null, task, name, 0, false);
Java 开发手册
15/44
System.out.println(thread.getName());
return thread;
}
}
3. 【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
说明:线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问
题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
4. 【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这
样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2) CachedThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
##线程池相关类图和几个重要的类
###Executors工具类 JDK内置的几种线程池可以使用Executors工厂类创建,扮演工厂的角色,也可以创建特定功能的线程池。
有且只有一个线程在工作,适合任务顺序执行,缺点是不能充分利用CPU多核的性能
public static ExecuyorService newSingleThreadExecutor(){
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
)
);
}
最大容量默认是Integer.MAX_VALUE,没有超时时间设置,适合能估算出多少核心线程的场景。 用于负载比较大的服务器,为了资源合理分配,限制线程数量
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(
nThreads,nThreads,0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
);
}
核心线程数是0,最大是Integer最大值,保活时间60S,这种队列不会存任务,智慧转发。适合执行大量的,轻量级任务。
public static ExecutorService newCachedThreadPool(){
return newThreadPoolExecutor(
0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,
new SynchronousQueue<Rannable>()
);
}
类似于定时器
public static ScheduleExecutorService newScheduleThreadPool(int corePoolSize){
return new ScheduleThreadPoolExecutor(corePoolSize);
}
###Executor接口 只有一个execute方法,用来替代创建或者启动线程的放大。
Thread t = new Thread();
executor.execute(t);
###ExecutorService接口 继承自Ececutor接口,提供了管理终止线程的方法,以及可为最总一个或者多个异步任务执行状况而生成的Future的方法。增加了shutDown(),shutDownNow(),invokeAll(),invockAny()和submit()等方法。
###SheduledExecutorService接口 扩展了ExecutorService接口并增加了schedule方法。调用schedule方法可以指定在延时指定间隔后执行一个Runnable或者Callable任务。提供了两个方法scheduleAtFixedRate(),scheculeWithFixedDelay()。
###ThreadPoolExecutor 继承自AbstractExecutorService,也实现了ExecutorService接口。
####ThreadPoolExecutor源码分析 几个重要字段
//(高三位)表示线程状态,(低三位)表示线程个数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING,0));
//线程个数掩码位数,对应平台下Integer的二进制位数-3剩余位数表示线程的个数
private static final int COUNT_BOTS=Integer.SIZE - 3;
//线程最大个数,低29位
private static final int CAPACITY = (1 << COUNT_BITS)-1;
//线程状态
private static final int RUNNING = -1 << COUNT_BITS; //高三位 111
private static final int SHUTDOWN = 0 << COUNT_BITS; //高三位 000
private static final int STOP = 1 << COUNT_BITS; //高三位 001
private static final int TIDYING = 2 << COUNT_BITS; //高三位 010
private static final int TERMINATED = 3 << COUNT_BITS; //高三位 011
/**
*ctl是对线程池的运行状态(runState)和有效线程数量(workerCount)进行控制的一个字段。
*workerCount的上限值大概是5亿。
*状态和线程数是通过位运算来提高效率的。
*/
当提交一个新的任务到线程池,线程池都会创建一个县的线程来执行此任务,无论线程池是否有空闲状态的线程。这种情况会持续到当任务数量大于线程池的基本线程数量大小时就不再创建了。也就是说新任务来的时候先检查是否超过核心线程数,没有就创建,有就放进等待队列。
如果队列满了,并且已经创建的线程数小于最大线程数,线程池会创建新的线程执行任务。本质上核心线程和非核心线程是没有区别的,也没有标识标记是否是核心线程。线程池只是用已有线程数和核心线程数以及最大线程数作比较决定下一步的策略。
线程池允许的线程空闲时间,当线程池的线程数量大于核心线程数的时候,没有新的任务继续提交,那么核心线程外的线程会等待一个keepAliveTime 的时间,超过等待时间就会销毁。 如果这个参数是0就会导致多余的线程在任务执行完之后立即终止。
可以使用TimeUnit 时间单位设置
用于设置创建新线程的工厂。默认使用Executors.defaultThreadFactory()来创建线程。使用默认的工厂创建线程时,会使用新建的具有相同的NORM_PRIORITY优先级并且是非守护线程,同时设置了线程的名称。
用于保存在等待执行任务的阻塞队列。等待任务队列用于存储当核心线程都在忙时,继续新增的任务,核心线程在处理完当前任务后,会去等待队列拉取任务继续执行。一般要求是线程安全的阻塞队列,容量根据业务选择。
ArrayBlockingQueue:基于数组的阻塞队列,按照FIFO原则进行排序
LinkedBlockingQueue:基于链表的阻塞队列,按照FIFO原则进行排序,吞吐量高于ArrayBlockingQueue。Executors.newFixedThreadPool()使用了这个队列。
SynchronousQueue:一个不存储元素的阻塞队列,每一个插入必须等待另外一个县城移除操作,否则插入状态一直处于阻塞状态。吞吐量高于LinkedBlockingQueue,Executors.nweCacheThreadPool使用了这个队列。
priorityBlockingQueue:一个具有优先级的无限阻塞队列.
本身是一个Java接口,当队列和线程池都满了,需要一种策略处理新来的任务。Java提供了四种实现,也可以自定义实现。 默认策略是丢弃策略
AbortPolicy:直接抛异常
CallerRunsPolicy:只在调用者所在的线程来运行任务
DiscardOldestPolicy:丢弃队列里最早的一个任务
DiscardPolicy:直接丢弃,没有任何其他操作
自定义:实现RejectExecutionHandler 接口
###ScheduledThreadPoolExecutor 继承自ThreadPoolExecutor,也实现了SheduledExecutorService接口。
原文:https://www.cnblogs.com/farmersun/p/12671640.html