目录
一、创建方式
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:核心线程数,即使线程空闲也不会被回收。
maximumPoolSize:最大线程数。最大线程数为Integer.MAX_VALUE
keepAliveTime:空闲线程的最大存活时间。
unit:线程存活的时间单位。
NANOSECONDS | 纳秒 |
MICROSECONDS | 微秒 |
MILLISECONDS | 毫秒 |
SECONDS | 秒 |
MINUTES | 分 |
HOURS | 时 |
DAYS | 天 |
workQueue:任务阻塞队列。
ArrayBlockingQueue:数组实现的有界阻塞队列,队中元素按FIFO排序,ArrayBlockingQueue在创建时必须指定其大小。
LinkedBlockingQueue:链表实现的阻塞队列,队中元素按FIFO排序。如果不设置LinkBlockingQueue的容量,则默认为Integer.MAX_VALUE,为无界队列。
PriorityBlockingQueue:具有优先级的无界队列。
DelayQueue:无界延迟阻塞队列,队中的每个元素都有过期时间,只有过期的元素才会出队。
SynchronousQueue:(同步队列)不存储元素的阻塞队列,不保存提交的任务,直接创建线程去执行新任务。
threadFactory:创建线程的工厂,制定线程的创建方式。当没有指定时会使用Executors.defaultThreadFactory()的默认实例。
handler:拒绝策略。 AbortPolicy:线程池默认的拒绝策略,如果线程池满,新任务会被拒绝,抛出RejectedExecutionException。
DiscardPolicy:如果线程池满,新任务会被丢掉,且不抛异常。
DiscardOldestPolicy:如果线程池满,会从最早进入队列的任务开始抛弃。
CallerRunsPolicy:如果线程池满,提交任务的线程自己去执行该任务,不会使用线程池中的线程去执行。
二、线程池的任务调度流程
a、当前工作线程数小于核心线程数时,即使其他工作线程处于空闲状态也会创建新的线程,直到线程数达到核心线程数。
b、在核心线程已经用完,但阻塞队列没有满的情况下,对于新接受的任务不会创建新线程,而是会被添加到阻塞队列中。
c、当一个任务执行结束时,执行器从阻塞队列中获取下一个任务开始执行,直到阻塞对列中的任务被取光。
d、当核心线程和阻塞对列均满的情况下,接受到新任务时会为新任务创建一个线程并立即执行。
e、当核心线程和阻塞对列均满且也已经达到了最大的线程数时,线程池会对新任务执行拒绝策略。
三、提交任务的两种方式
execute()
submit()
区别:
(1)、execute只接受Runnable类型参数,submit可以接受Callable,Runnable类型的参数。
(2)、submit有返回值,而execute没有
(3)、submit可以进行异常捕获,而execute不行。
例子:通过submit()返回的Future对象获取返回结果
package threadPackage;
import java.util.concurrent.*;
public class FutureBack {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolExecutor pool = new ThreadPoolExecutor(1,
1,
1L,
TimeUnit.SECONDS,
new SynchronousQueue(),
Executors.defaultThreadFactory());
Future<Integer> submit = pool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return 100;
}
});
System.out.println(submit.get());
pool.shutdown();
}
}
结果:
100
例子:submit()返回的Future对象捕获异常
package threadPackage;
import java.util.concurrent.*;
public class FutureBack {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolExecutor pool = new ThreadPoolExecutor(1,
1,
1L,
TimeUnit.SECONDS,
new SynchronousQueue(),
Executors.defaultThreadFactory());
Future<Integer> submit = pool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
throw new RuntimeException("ERROR");
}
});
try {
Integer integer = submit.get();
System.out.println(integer);
}catch (Exception re){
System.out.println(re.getMessage());
}
pool.shutdown();
}
}
结果:
java.lang.RuntimeException: ERROR
四、调度器的钩子方法
package threadPackage;
import java.util.concurrent.*;
public class Pool extends ThreadPoolExecutor{
public Pool() {
super(5, 5, 0L, TimeUnit.SECONDS, new SynchronousQueue<>());
}
// 线程执行前运行
@Override
protected void beforeExecute(Thread t, Runnable r) {
System.out.println("beforeExecute doing: " + Thread.currentThread().getName());
super.beforeExecute(t, r);
}
// 线程执行后
@Override
protected void afterExecute(Runnable r, Throwable t) {
System.out.println("afterExecute doing: " + Thread.currentThread().getName());
super.afterExecute(r, t);
}
// 调度器终止后
@Override
protected void terminated() {
System.out.println("terminated doing: " + Thread.currentThread().getName());
super.terminated();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Pool pool = new Pool();
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + " doing");
}
});
Thread.sleep(1000);
pool.shutdown();
}
}
结果:
beforeExecute doing: pool-1-thread-1
pool-1-thread-1 doing
afterExecute doing: pool-1-thread-1
terminated doing: pool-1-thread-1
可以在线程执行前后获取当前系统时间,计算线程执行时间。
System.currentTimeMillis()
五、线程池的5种状态
源码:
public class ThreadPoolExecutor extends AbstractExecutorService {
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
}
RUNNING:线程池创建的初始状态,该状态下可以执行任务。
SHUTDOWN:该状态下线程池不在接受新任务,但会将工作队列中的任务执行完成。
STOP:该状态下线程池不在接受新任务,也不处理工作队列中的任务,会中断所有的工作线程。 TIDYING:该状态下所有的任务都已终止或者执行完成,将执行terminated()方法。
TERMINATED:执行terminated()方法后的状态。 线程池状态之间的转换图 shutdown():此方法会等待队列中的任务执行完成后才执行关闭。
shutdownNow():立即关闭线程池,中断正在执行的线程,清空队列中的任务,返回尚未执行的任务。
awaitTermination():调用shutdown()或者shutdownNow()方法时,当前线程会立即返回,而awaitTermination()会等待线程池完成关闭。
六、优雅的关闭线程池
package threadPackage;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadUtil {
public static void shutdownThreadPoolGracefully(ExecutorService pool){
// 若线程池已关闭则返回
if(pool.isTerminated()){
return;
}
try {
// 循环等待1000次,每次等待10毫秒
for (int i=0; i< 1000;i++){
if (!pool.awaitTermination(10, TimeUnit.MILLISECONDS)) {
break;
}
// 立即关闭
pool.shutdownNow();
}
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
七、确定线程池的线程数
a、IO密集型任务:此类任务主要是执行IO操作。
需要开CPU核心数2倍的线程。
package threadPackage;
import java.util.concurrent.*;
public class ThreadUtil {
// CPU核数
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// IO处理线程数
public static final int IO_MAX = Math.max(2, CPU_COUNT * 2);
// 存活时间,秒
public static final int KEEP_ALIVE_SECONDS = 30;
// 有界队列的size
public static final int QUEUE_SIZE = 128;
private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
IO_MAX,
IO_MAX,
KEEP_ALIVE_SECONDS,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(QUEUE_SIZE),
Executors.defaultThreadFactory()
);
}
b、CPU密集型任务:此类任务主要是执行计算任务。
线程数等于cpu核心数
package threadPackage;
import java.util.concurrent.*;
public class ThreadUtil {
// CPU核数
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// IO处理线程数
public static final int POOL_SIZE = CPU_COUNT;
// 存活时间,秒
public static final int KEEP_ALIVE_SECONDS = 30;
// 有界队列的size
public static final int QUEUE_SIZE = 128;
private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
POOL_SIZE,
POOL_SIZE,
KEEP_ALIVE_SECONDS,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(QUEUE_SIZE),
Executors.defaultThreadFactory()
);
}
c、混合型任务:既有计算又有IO操作,例如http请求。
最佳线程数=(线程等待时间 + 线程cpu时间)/ 线程CPU时间 * CPU核数。
线程等待时间:DB操作,RPC操作,缓存操作
线程cpu时间:平均线程CPU运行时间