一、线程池的状态
RUNNING: 可以接收新任务;可以处理阻塞队列任务
SHUTDOWN: 不会接收新任务;但会处理阻塞队列剩余任务
STOP: 会中断正在执行的任务;并抛弃阻塞队列任务
TIDYING: 任务全部执行完毕,活动线程为0,即将进入终结
TERMINATED: 终结
二、ThreadPoolExecutor构造方法
ThreadPoolExecutor是jdk提供的线程池实现,先看一下全参数的构造方法
public ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//救急线程空闲时间
TimeUnit unit,//空闲时间单位
BlockingQueue<Runnable> workQueue,//任务队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler // 拒绝策略) {
//构造方法
}
救急线程:核心线程全部繁忙任务队列也满了,就会启动救急线程来执行新添加到线程池的任务,当救急线程的空闲时间到达给定的时间时就会自动结束
救急线程数=最大线程数-核心线程数
任务队列:当线程池核心线程繁忙时存储提交到线程池的新任务
线程工厂: 给线程池指定创建线程的方式
拒绝策略:线程全部繁忙,任务队列也满时线程池怎样处理新添加的任务
public interface ThreadFactory {
/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}
示例
以下创建了一个线程,核心线程数是1,救急线程1,任务队列的容量是1,使用默认的线程工厂和拒绝策略
public class Test8 {
static Logger LOG = LoggerFactory.getLogger(Test8.class);
public static void main(String[] args) {
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1,2,0, TimeUnit.MILLISECONDS,new LinkedBlockingDeque<>(1));
executor.execute(new Runnable() {
@Override
public void run() {
LOG.info("AAA");
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
executor.execute(new Runnable() {
@Override
public void run() {
LOG.info("BBB");
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//这个任务线程池会创建救急线程执行,而B还在任务队列中等待,所以C会先执行
executor.execute(new Runnable() {
@Override
public void run() {
LOG.info("CCC");
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
三、Executors工具类中创建线程池的方法
3.1 newFixedThreadPool(int nThreads) 创建固定大小的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
从这个方法的源码可以看出这样创建的线程池的特点:
没有救急线程,任务队列是无界的(容量是Integer类型的最大值),任务较多时有可能造成内存溢出;使用的是默认的拒绝策略
3.2 newCachedThreadPool 带缓冲的线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
这个线程池中的所有线程都是救急线程,救急线程的个数是Integer的最大值任务执行完后1min内没有新的任务就会结束运行。
SynchronousQueue 没有容量,没有线程来取是放不进去的,没有线程取put方法就会阻塞住。
这个线程池的特点是可以创建无限多的线程来执行任务,任务执行完后线程可以自动结束。
适合任务数比较密集,但每个任务执行时间较短的情况。
3.3 newSingleThreadExecutor 单线程线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
救急线程数为0,核心线程是1
场景:希望多个任务排队执行,线程数固定为1其余任务会放入无界队列排队。
和自己创建一个线程来执行任务的区别:
自己创建的线程如果任务中发生异常线程就会结束而没有补救措施,其余任务就不能执行了;线程池中如果线程挂掉了会启动新的线程来保证池的正常工作,后续任务还可以继续执行。
和new newFixedThreadPool(1)的区别:
返回的对象不同,new newFixedThreadPool(1)对外暴露的是ThreadPoolExecutor,还可以通过set方法修改核心线程数,而newSingleThreadExecutor返回的对象不能被修改。
四、拒绝策略
当任务队列满了,核心线程和救急线程都繁忙时,线程会应用拒绝策略
4.1 AbortPolicy
抛出异常
4.2 CallerRunsPolicy
调用者自己来执行任务
4.3 DiscardPolicy
放弃新提交任务,什么都不做
4.4 DiscardOldestPolicy
丢弃任务队列中等待时间最长的一个任务,把当前任务提交进去
五、提交任务的方法
5.1 void execute
5.2 Future task)
5.3 List<Future> invokeAll(Collection<? extends Callable> tasks)
可以用来利用返回的Future对象等待多个任务执行完
5.4 invokeAny(Collection<? extends Callable> tasks)
可以用来利用返回的Future对象等待其中时间最短的任务执行完,其余的任务会被丢弃,包含任务队列中的任务和正在执行的任务(被中断)
六、关闭线程池的方法
6.1 shutdown
线程池状态变为SHUTDOWN,不会接收新任务,但已提交的任务(任务队列中的任务)会执行完,不会阻塞调用线程的执行。
6.2 shutdownNow
线程池状态变为STOP,不会接收新任务,会将队列中的任务返回,不再执行,用interrupt中断正在执行的任务