线程池大小的设置一直是在开发中比较难的点,网上没有找到一个比较合适的设置的方案。
这个是美团技术整理一份关于网上比较多的一些线程设置方案。
按照网上的方案设置线程池的大小,基本都是对线程池的大小偏高。下面是美团大佬给出的方案。
这篇博客,主要在这个方案下,写一下代码方面的如果改变比较关注的核心线程、最大线程数、队列长度的调整。这里在调整整个线程池大小的时候有两个需要注意点:
1、jdk 的BlockingQueue 的capacity 是final修饰的。
其实ThreadPoolExecutor提供设置最大线程池和核心线程的大小
executor.setMaximumPoolSize(20);
executor.setCorePoolSize(10);
但在队列长度设置的的key发现jdk设置容量的地方,
可以看到阻塞队列的长度是 final修饰的,无法改变其容量大小,因此为了改变容量大小。我们可以自定义阻塞队列,只需要改将阻塞队列改一个名字,并将其去掉final修饰。
2、在ThreadPoolExecutor#getTask的时候,如果当前工作线程大于核心最大线程,不能够获取到runnable任务。
如果当前工作线程大于核心最大线程,不能够获取到runnable任务,因此在设置最大线程数和核心线程的大小的时候,需要先设置最大线程数,再去设置核心线程数。
下面是代码的具体实现:
package com.yin.freemakeradd.utils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.*;
/**
* @author yin
* @Date 2020/4/25 16:51
* @Method
*/
public class DynamicThreadPool {
/**
* 构建一个普通线程池
* @return
*/
private static ThreadPoolExecutor buildThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
DynamicLinkedBlockingQueue<Runnable> workQueue,
String threadName){
return new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
keepAliveTime,unit, workQueue, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName(threadName);
thread.setDaemon(true);
return thread;
}
});
}
public static void main(String[] args) throws InterruptedException {
dynamicThreadPool();
}
private static void dynamicThreadPool() throws InterruptedException {
ThreadPoolExecutor executor = buildThreadPoolExecutor(2, 5,
60, TimeUnit.SECONDS, new DynamicLinkedBlockingQueue<>(10), "dynamicPoolTestThread");
for (int i = 0; i < 15; i++) {
final int j = i;
executor.submit(()->{
printThreadPoolStatus(executor, "创建任务");
try {
TimeUnit.SECONDS.sleep(10+2*j);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
sleepSeconds(1);
printThreadPoolStatus(executor, "完成任务创建");
DynamicLinkedBlockingQueue<Runnable> queue =(DynamicLinkedBlockingQueue) executor.getQueue();
queue.setCapacity(20);
executor.setMaximumPoolSize(20);
executor.setCorePoolSize(10);
sleepSeconds(1);
printThreadPoolStatus(executor, "改变设置之后");
Thread.currentThread().join();
}
private static void sleepSeconds(int time){
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS");
private static void printThreadPoolStatus(ThreadPoolExecutor executor, String name) {
BlockingQueue<Runnable> queue = executor.getQueue();
System.out.println(formatter.format(LocalDateTime.now())+"::"+name + "::" + Thread.currentThread().getName() + "::" +
"核心线程数 :" + executor.getCorePoolSize() +
"::最大线程数 :" + executor.getMaximumPoolSize() +
"::活动线程数 :" + executor.getActiveCount()+
"::任务完成数"+executor.getCompletedTaskCount()+
"::队列使用 :"+queue.size()+"::队列未使用 :"+queue.remainingCapacity()+"::队列总共大小 :"+(queue.size()+queue.remainingCapacity()));
}
}
自定义队列:
/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
*
*
*
*
*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
/**
* An optionally-bounded {@linkplain BlockingQueue blocking queue} based on
* linked nodes.
* This queue orders elements FIFO (first-in-first-out).
* The <em>head</em> of the queue is that element that has been on the
* queue the longest time.
* The <em>tail</em> of the queue is that element that has been on the
* queue the shortest time. New elements
* are inserted at the tail of the queue, and the queue retrieval
* operations obtain elements at the head of the queue.
* Linked queues typically have higher throughput than array-based queues but
* less predictable performance in most concurrent applications.
*
* <p>The optional capacity bound constructor argument serves as a
* way to prevent excessive queue expansion. The capacity, if unspecified,
* is equal to {@link Integer#MAX_VALUE}. Linked nodes are
* dynamically created upon each insertion unless this would bring the
* queue above capacity.
*
* <p>This class and its iterator implement all of the
* <em>optional</em> methods of the {@link Collection} and {@link
* Iterator} interfaces.
*
* <p>This class is a member of the
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
*
* @param <E> the type of elements held in this collection
* @author Doug Lea
* @since 1.5
*/
public class DynamicLinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
private static final long serialVersionUID = -6903933977591709194L;
/*
* A variant of the "two lock queue" algorithm. The putLock gates
* entry to put (and offer), and has an associated condition for
* waiting puts. Similarly for the takeLock. The "count" field
* that they both rely on is maintained as an atomic to avoid
* needing to get both locks in most cases. Also, to minimize need
* for puts to get takeLock and vice-versa, cascading notifies are
* used. When a put notices that it has enabled at least one take,
* it signals taker. That taker in turn signals others if more
* items have been entered since the signal. And symmetrically for
* takes signalling puts. Operations such as remove(Object) and
* iterators acquire both locks.
*
* Visibility between writers and readers is provided as follows:
*
* Whenever an element is enqueued, the putLock is acquired and
* count updated. A subsequent re