线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后再创建线程后启动这些任务。线程池都是后台线程,每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元。如果某个线程再托管代码中空闲 (如正在等待某事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超出最大值得线程可以排队,但他们要等待其它线程完成后才启动。
创建线程数量没有限制,可以灵活回收线程,如果设置线程空闲时间1分钟,则该工作线程自动终止,终止后后提交了新任务,就会再重新建立一个线程。
缺点是在使用CachedThreadPool时,一定要注意控制任务的数量,否则由于大量线程同时运行,很有可能会造成系统瘫痪。
核心线程数为0,非核心线程数为MAX_VALUE
队列不存储值,总认为队列是满的,所以每次执行任务都会创建非核心线程,非核心线程空闲了超过60秒(默认),就会自动回收。
示例
private static ExecutorService threadpool = Executors.newCachedThreadPool();
@Test
public void testNewCachedThreadPool() throws InterruptedException {
for (int i = 0; i < 10; i++) {
final int index = i;
Thread.sleep(1000);
threadpool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " start ===" + index);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end ===" + index);
}
});
}
Thread.sleep(200000);
}
可以看到它的线程池中线程数量并不固定,如果有新任务进来,它会自动扩充线程池大小并启动,如果任务结束,它的池大小则会缩减。
在达到长度之前,每提交一个任务都会创建一个线程,如果达到了线程池最大数量,则提交到队列中,在空闲的时候也不会自动回收线程。
核心线程数位为参数传入,非核心线程数和核心线程数一样
队列为无界队列,资源有限的时候容易引起OOM
与newSingledTheadPool不同的是核心线程数不为1
示例
private static ExecutorService threadpool2 = Executors.newFixedThreadPool(3);
@Test
public void testNewFixedThreadPool() throws InterruptedException {
for (int i = 0; i < 10; i++) {
final int index = i;
Thread.sleep(1000);
threadpool2.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " start ===" + index);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end ===" + index);
}
});
}
Thread.sleep(200000);
}
可以看到它的线程池是固定长度的,当占用数量达到线程池长度的时候,后面的线程需要等待线程池资源的释放,才能获取执行机会。
只有一个线程按顺序执行任务,如果这个线程出现异常结束,会有另一个线程取代并按顺序执行。
核心线程数为1,非核心线程数为1
队列为无界队列
单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定时间不会有多个线程是活动的。
示例
private static ExecutorService threadpool3 = Executors.newSingleThreadExecutor();
@Test
public void testNewSingleThreadExecutor() throws InterruptedException {
for (int i = 0; i < 10; i++) {
final int index = i;
Thread.sleep(1000);
threadpool3.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " start ===" + index);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end ===" + index);
}
});
}
Thread.sleep(200000);
}
可以看到它的线程池长度固定为1,同一时刻只有一个线程在执行,不存在多线程资源争夺的问题。
创建一个定长的线程池,而且支持定时的以及周期性的任务执行。如延迟3秒执行或者每隔3秒执行一次。
核心线程数为参数设定,非核心线程数为MAX_VALUE
定义了一个DelayedWorkQueue,它是一个有序队列,会通过每个任务按照距离下次执行时间的间隔的大小来排序。
(1) 定长线程池特性
示例
private static ExecutorService threadPool4 = Executors.newScheduledThreadPool(2);
@Test
public void testNewScheduledThreadPool() throws InterruptedException {
for (int i = 0; i < 10; i++) {
final int index = i;
Thread.sleep(1000);
threadPool4.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " start ===" + index);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end ===" + index);
}
});
}
Thread.sleep(200000);
}
执行结果,可以看到它定长线程池的特性,具有newFixedThreadPool 的特性。
(2) 延迟特性
private static ScheduledExecutorService threadPool5 = Executors.newScheduledThreadPool(2);
@Test
public void testNewScheduledThreadPool2() throws InterruptedException {
System.out.println("-------"+ DateUtils.parseDate(new Date()));
for (int i = 0; i < 10; i++) {
final int index = i;
// Thread.sleep(1000);
threadPool5.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " "+DateUtils.parseDate(new Date())+" start ===" + index);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end ===" + index);
}
},5, TimeUnit.SECONDS);
}
Thread.sleep(200000);
}
可以看到线程池在延迟5秒后启动执行
(3)延迟+周期特性
@Test
public void testNewScheduledThreadPool3() throws InterruptedException {
System.out.println("-------"+ DateUtils.parseDate(new Date()));
threadPool5.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " "+DateUtils.parseDate(new Date())+" start ===");
}
},8,5, TimeUnit.SECONDS);
Thread.sleep(200000);
}
运行可以看到,线程延迟8秒开始第一次执行,之后以每5秒执行一次
判断核心线程数是否已满,核心线程数大小和corePoolSize参数有关,未满则创建线程执行任;若核心线程池已满,判断队列是否满,队列是否满与workQueue参数有关,若未满则加入队列中;如果队列已满,判断线程池是否已满,线程池是否已满和maximunPoolSize参数有关;若未满创建线程执行任务,若干线程池已满,则采用拒绝策略处理无法执行的任务,拒绝策略与handler参数有关。