创建新线程,需要和操作系统进行交互,成本比较高。
一、基础概念
KeepAliveTime:是多余的线程等待新任务的最长时间,超过这个时间多余的线程将被终止
workQueue:任务存储队列。
3种常见的队列:
1. 直接交接 SynchronousQueue。SynchronousQueue的capacity是0,即不存储任何元素。
2. 无界队列 LinkedBlockingQueue。
3. 无界队列 ArrayBlockingQueue
threadFactory: 指定创建线程的工厂,一般不用指定,用默认的defaultThreadFactory即可。
二、几种线程池结构
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
即核心线程数和最大线程数保持一致,因为是固定线程数,所以即使是线程空闲状态也不进行回收,所以KeepAliveTime也为0,任务队列采用LinkedBlockingQueue无界队列,因为当线程数超过最大线程数以后,需要将所有新增的线程放到无界队列里。
OOM异常原因:线程不断新增并且阻塞,导致无界队列占用内存超过锁分配的内存。
实现:
public class FixedThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 3, 0, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
for (int i = 0; i < 100; i++) {
threadPoolExecutor.execute(new MyRunnable());
}
}
static class MyRunnable implements Runnable {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println("当前线程名:" + name);
}
}
}
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
缓存线程池的核心线程数为0,最大线程数Integer.MAX_VALUE,即可以理解可以不断创建新的线程,任务队列使用SynchronousQueue,SynchronousQueue的容量为0,即不存线程.
OOM原因:每个线程任务都要新增一个线程,每个线程都要占用内存资源,一定数量导致OOM。
ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
ScheduledThreadPoolExecutor主要用来在给定的延迟之后运 行任务,或者定期执行任务。由于队列使用的是DelayQueue是一个无界队列,所以这里的最大线程数maximumPoolSize没有什么意义。
实现:
public class ScheduledThreadPoolTest {
public static void main(String[] args) {
System.out.println("开始执行!");
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
//1.延迟5秒钟执行
//scheduledExecutorService.schedule(new MyRunnable(),5, TimeUnit.SECONDS);
//2.初始延迟3秒钟开始执行,每5秒钟执行一次
scheduledExecutorService.scheduleAtFixedRate(new MyRunnable(),3,5,TimeUnit.SECONDS);
}
static class MyRunnable implements Runnable {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println("当前线程名:" + name);
}
}
}
三、停止线程池的方法
void shutdown();
boolean isShutdown();
List<Runnable> shutdownNow();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit);
shutdown()
public class ShutDownTest {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
for (int i = 0; i < 1000; i++) {
threadPoolExecutor.execute(new MyTask());
}
threadPoolExecutor.shutdown();
threadPoolExecutor.execute(new MyTask());
}
static class MyTask implements Runnable{
@Override
public void run() {
try {
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被中断了");
}
}
}
}
当执行了shutdown方法,1000个线程有995个在队列里,队列里的任务仍然会执行,只是不能再执行新的线程。
isShutdown()
//当执行了shutdown方法以后,可以通过该方法判断线程池是否停止。
executorService.isShutdown()
isTerminated()
boolean flag=executorService.isTerminated()
isTerminated()与isShutdown()方法的区别在于:isShutdown()只要执行了shutdown()方法,就会返回true,而isTerminated()方法需要等延迟队列里所有的任务执行完成才会返回true。
awaitTermination()
boolean b = executorService.awaitTermination(7L, TimeUnit.SECONDS);
上述代码表示7秒钟之内,判断线程池是否仍有未执行完成的线程。
shutdownNow()
public class ShutdownNowTest {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
for (int i = 0; i < 1000; i++) {
threadPoolExecutor.execute(new MyTask());
}
Thread.sleep(1500);
List<Runnable> runnables = threadPoolExecutor.shutdownNow();
}
static class MyTask implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被中断了");
}
}
}
}
//结果如下:
pool-1-thread-5
pool-1-thread-3
pool-1-thread-4
pool-1-thread-2
pool-1-thread-5
pool-1-thread-1
pool-1-thread-3
pool-1-thread-4被中断了
pool-1-thread-5被中断了
pool-1-thread-1被中断了
pool-1-thread-3被中断了
pool-1-thread-2被中断
可以看到,正在执行的核心线程立即都被中断了,同时,该方法返回队列里所有未执行的线程,这样可以将未执行的进行日志记录等等。