线程池的重要性
- 复用每一个线程
- 控制线程的数量
为什么使用线程池
- 反复创建线程开销大,使用线程池可以重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 过多的线程会占用太多的内存。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
线程池的优势
- 加快响应速度,不用反复创建与销毁线程。
- 合理利用CPU和内存,灵活调整线程数量而不至于内存溢出。
- 利用线程池可以统一管理资源。
线程池结构
线程池结构适用场景
- 服务器接受到大量请求时,使用线程池技术是非常合适的,它可以大大减少线程的创建和销毁次数,提高服务器的工作效率。
- 在实际开发中,如果需要创建5个以上的线程,那么就可以使用线程池来管理。
创建和停止线程池
线程池参数介绍:
corePoolSize
指的是核心线程数:线程池在完成初始化后,默认情况下,线程池中并没有任何线程,线程池会等待有任务到来时,在创建新线程去执行任务。
maximumPoolSize
线程池允许创建的最大线程数,超过就reject
;线程池有可能会在核心线程数的基础上,额外增加一些线程,但是这些新增加的线程数有一个上限。如果队列满了,并且已创建的线程数小于
最大线程数,则线程池会再创建
新的线程执行任务。
添加线程规则
- 如果线程数小于corePoolSize,即使其他工作线程处于空闲状态,也会创建一个新线程来运行新任务。
- 如果线程数等于(或大于)corePoolSize但少于maximumPoolSize,则将任务放入队列。
- 如果队列已满,并且线程数小于maximumPoolSize,则创建一个新线程来运行任务。
- 如果队列已满,并且线程数大于或等于maximumPoolSize,则拒绝该任务。
举个例子
线程池:核心池大小为5,最大池大小为10,队列100。
因为线程中的请求最多会创建5个,然后任务将会被添加到队列中,直到达到100。当队列已满时,将创建最新的线程maxPoolSize,最多到10个线程,如果再来任务,则拒绝。
增减线程的特点
- 通过设置corePoolSize和maximumPoolSize相同,就可以创建固定大小的线程池。
- 线程池
希望保持较少的线程数
,并且只有在负载变得很大时才增加它。 - 通过设置maximumPoolSize为很高的值,例如Integer.MAX_VALUE,可以允许线程池容纳任意数量的并发任务。
- 只有在队列填满时才创建多于corePoolSize的线程,所以如果你使用的是无界队列(LinkedBlockingQueue),那么线程数就不会超过corePoolSize。
keepAliveTime
如果线程池当前的线程数多于corePoolSize,而且多余的线程空闲时间超过keepAliveTime,他们就会被终止。
threadFactory
新的线程是由ThreadFactory
创建的,默认使用Executors.defaultThreadFactory()
, 创建出来的线程都在同一个线程组,拥有同样的优先级并且都不是守护线程,如果自己指定ThreadFactory
,那么就可以改变线程名,线程组,优先级,是否是守护线程,通常默认即可。
工作队列
有3种最常见的队列类型:
-
直接交换。SynchronousQueue。对于这样的队列,是无法存储任务的,这也就意味着我们在使用时需要把maximumPoolSize设置大点。
-
无界队列。LinkedBlockingQueue。对于这样的队列,我们可以存储很多任务,但如果线程处理不及时时,容易造成极大的内存浪费。
-
有界队列。ArrayBlockingQueue
线程数量设定(粗略估计)
- CPU密集型(加密,HASH计算等):最佳线程数为CPU核心数的1-2倍左右。
- 耗时IO型(读写数据库,文件,网络读写等):最佳线程数一般会大于cpu核心数很多倍,以JVM线程监控显示繁忙情况为依据,保证线程空闲可以衔接上,参考Brain Goetz推荐的计算方法:
线程数 = CPU核心数 * (1 + 平均等待时间/平均工作时间)
手动创建VS自动创建
手动创建更好,因为这样可以让我们更加明确线程池的运行规则,避免资源耗尽的风险。
停止线程池
- shutdown
执行完shutdown()方法后,线程池将会拒接新的任务,在执行完本次队列里的所有任务后停止线程池.
注意:并非立即停止,在收到shutdown信号后,线程池会在完成队列里的所有任务后终止线程池,也就是shutdown信号之前队列里的所有任务。
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executorService.execute(new ShutDownTask());
}
Thread.sleep(1500);
// 停止线程池
executorService.shutdown();
// 再次提交任务时,线程池拒接新的任务
executorService.execute(new ShutDownTask());
}
}
class ShutDownTask 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() + "被中断了");
}
}
}
-
isShutdown
通过bool值告知线程是否停止
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executorService.execute(new ShutDownTask());
}
Thread.sleep(1500);
// 打印当前线程池的状态 // false
System.out.println(executorService.isShutdown());
// 停止线程池
executorService.shutdown();
// 再次打印当前线程池的状态 // true
System.out.println(executorService.isShutdown());
}
}
class ShutDownTask 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() + "被中断了");
}
}
}
- isTerminated
返回整个线程当前运行状态是否终止
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.execute(new ShutDownTask());
}
Thread.sleep(1500);
// 打印当前线程池的状态 // false
System.out.println(executorService.isShutdown());
// 停止线程池
executorService.shutdown();
// 再次打印当前线程池的状态 // true
System.out.println(executorService.isShutdown());
// 获取当前线程运行状态:false
System.out.println(executorService.isTerminated()); // false
// 15s后获取当前线程运行状态:true
Thread.sleep(15000);
System.out.println(executorService.isTerminated()); // true
}
}
class ShutDownTask 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() + "被中断了");
}
}
}
- awaitTermination
测试一段时候后线程是否终止,执行完毕返回true,反之false。本方法会在以下3种情况会返回结果:
- 线程池执行结束。
- 等待的时间到。
- 线程被中断。
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.execute(new ShutDownTask());
}
Thread.sleep(1500);
executorService.shutdown();
// 检查7s中后线程池是否停止
boolean b = executorService.awaitTermination(7L, TimeUnit.SECONDS);
System.out.println(b);
}
}
class ShutDownTask 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() + "被中断了");
}
}
}
- shutdownNow
立刻关闭线程池
package threadpool;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.execute(new ShutDownTask());
}
Thread.sleep(1500);
// 返回队列里未完成的任务
List<Runnable> runnableList = executorService.shutdownNow();
System.out.println(runnableList);
}
}
class ShutDownTask 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() + "被中断了");
}
}
}
总结:
可以通过调用线程池的shutdown
或shutdownNow
方法来关闭线程池。它们的原理
是遍历线程池中的工作线程
,然后逐个调用线程的interrupt方法
来中断线程
,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow
首先将线程池的状态设置成STOP
,然后尝试停止所有的正在执行或暂停任务的线程
,并返回等待执行任务的列表
;而shutdown
只是将线程池的状态设置成SHUTDOWN状态
,然后中断所有没有正在执行任务
的线程。只要调用了这两个关闭方法中的任意一个,isShutdown
方法就会返回true
。当所有的任务都已关闭
后,才表示线程池关闭成功
,这时调用isTerminaed
方法会返回true
。至于应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown
方法来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow
方法。
网友评论