自定义线程池的名称(ThreadPoolExecutor)
目的:有时候为了快速定位出现错误的位置,在采用线程池时我们需要自定义线程池的名称。
1、创建ThreadFactory(ThreadPoolExecutor默认采用的是DefaultThreadFactory,可以参照代码)。
public class NamedThreadFactory implements ThreadFactory{ private final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup threadGroup; private final AtomicInteger threadNumber = new AtomicInteger(1); public final String namePrefix; NamedThreadFactory(String name){ SecurityManager s = System.getSecurityManager(); threadGroup = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); if (null==name || "".equals(name.trim())){ name = "pool"; } namePrefix = name +"-"+ poolNumber.getAndIncrement() + "-thread-"; } @Override public Thread newThread(Runnable r) { Thread t = new Thread(threadGroup, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } }
2、创建线程池
//核心线程满了,则进入队列,队列满了,则创建新线程,当线程数达到最大线程数,则进入拒绝策略
static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,5,1, TimeUnit.MINUTES,new LinkedBlockingDeque<>(),new NamedThreadFactory("测试"));
3、测试代码
static ThreadLocal<SimpleDateFormat>threadLocal = new ThreadLocal<SimpleDateFormat>(){ @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } }; public static void main(String[] args) { threadPoolExecutor.execute(new Runnable() { @Override public void run() { try { System.out.println(threadLocal.get().parse("2019-10-22 16:59:00")); throw new NullPointerException("sfa"); } catch (ParseException e) { e.printStackTrace(); } } }); }
4、结果
补充:
1、参数解析
2、拒绝策略
(1)、CallerRunsPolicy
该任务被线程池拒绝,由调用execute方法的线程(如main线程)执行该任务。一般并发比较小,性能要求不高,不允许失败。但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大。
(2)、AbortPolicy
丢弃任务,并抛出 RejectedExecutionException 异常。线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行。
(3)、DiscardOldestPolicy
丢弃任务,不过也不抛出异常。
(4)、DiscardOldestPolicy
当触发拒绝策略,只要线程池没有关闭的话,丢弃阻塞队列 workQueue 中最老的一个任务,并将新任务加入。
3、线程池如何配置合理线程数
(1)cpu密集型
定义:CPU密集型的意思就是该任务需要大量运算,而没有阻塞,CPU一直全速运行。
CPU密集型任务只有在真正的多核CPU上才可能得到加速(通过多线程)。
CPU密集型任务配置尽可能少的线程数。
CPU密集型线程数配置公式:(CPU核数+1)个线程的线程池
获取操作系统的cpu核数:Runtime.getRuntime().availableProcessors()
(2)IO密集型
定义:IO密集型,即该任务需要大量的IO,即大量的阻塞。
在单线程上运行IO密集型任务会导致浪费大量的CPU运算能力浪费在等待。
所以IO密集型任务中使用多线程可以大大的加速程序运行,即使在单核CPU上,这种加速主要利用了被浪费掉的阻塞时间。
第一种配置方式:
由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程。
配置公式:CPU核数 * 2
第二种配置方式:
IO密集型时,大部分线程都阻塞,故需要多配置线程数。
配置公式:CPU核数 / (1 – 阻塞系数)(0.8~0.9之间)
比如:8核 / (1 – 0.9) = 80个线程数