newThread的弊端
(1)每次new Thread新建对象,性能较差
(2)线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多的系统资源导致死机或者OOM
(3)缺少更多得功能,如更多执行,定期执行,线程中断
线程池的好处
(1)重用存在的线程,减少对象的创建,消亡的开销,性能好
(2)可以有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞
(3)提供定时执行,定期执行,单线程,并发数控制等功能
线程池 - ThreadPoolExecutor
一些重要参数,以及处理时的情况:
1.corePoolSize : 核心线程数量。
2.maximumPoolSize : 线程最大线程数。
3.workQueue : 阻塞队列,储存等待执行的任务,很重要,会对线程池运行过程产生重大影响。
1.运行的线程数小于corePoolSize的时候,直接创建新线程来执行任务,即使其他线程池的其它线程是空闲的。
2.运行的线程数大于等于corePoolSize且小于maximumPoolSize时只有当workQueue满的时候才创建新的线程来执行任务。
3.如果设置的corePoolSize等于maximumPoolSize,则创建的线程池大小是固定的。
4.如果运行的线程数等于maximumPoolSize,且workQueue还没有满,则新任务会添加到workQueue中。
5.如果运行线程数等于maximumPoolSize,且workQueue已经满了,则通过拒绝策略的参数来处理这个任务。
keepAliveTime : 线程没有任务执行,最多保持多长时间终止
unit : keepAliveTime的时间单位
threadFactory : 线程工厂,用来创建线程
rejectHandler : 当拒绝处理任务时的策略(1.抛出异常默认策略2.用调用者所在的线程执行任务3.丢弃队列中最靠前的任务来执行当前任务4.丢弃任务)
可以根据自己的使用场景,来设置审核自己的参数。
execute()方法:提交任务,交给线程池执行
submit()方法:提交任务,能够返回执行结果
shutdown()方法:关闭线程池,等待任务都执行完成
shutdownNow()方法:关闭线程池,不等待任务完成
getTaskCount()方法:线程池已经执行和未执行的任务总数
getCompletedTaskCount()方法:已经完成的任务数量
getPoolSize()方法:线程池当前的线程数量
getActiveCount()方法:当前线程池正在执行任务的线程数量
根据JDK给我们提供这些方法,我们就可以实时的监控线程池的工作状态。
线程池的创建方式
一般有两种方式创建线程池,一种是直接调用构造方法,另一种是使用Executors去创建。《阿里巴巴Java开发手册》强制不允许使用Executors创建,因为这样存在一些弊端。
通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
Executors 返回线程池对象的弊端如下:
FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
使用Executors 创建线程池:
1.Executors.newCachedThreadPool():
创建一个可以缓存的线程池,返回一个可根据实际情况调整线程数量的线程池。 线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程 在当前任务执行完毕后,将返回线程池进行复用。
2.Executors.newFixedThreadPool(10):
该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
3.Executors.newScheduledThreadPool(10):
该方法返回一个固定线程数量的线程池。支持定长,周期性的任务执行。
4.Executors.newSingleThreadExecutor():
方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
CachedThreadPool例子:
@Slf4j
public class ThreadPoolExample1 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(() -> {
log.info("task:{}",index);
});
}
executorService.shutdown();
}
}
结果是不按大小顺序打印的,这是因为newCachedThreadPool没有限制最大线程数。
FixedThreadPool例子:
@Slf4j
public class ThreadPoolExample2 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(() -> {
log.info("task:{}",index);
});
}
executorService.shutdown();
}
}
可以设置一个固定的线程数目。
FixedThreadPool例子:
@Slf4j
public class ThreadPoolExample3 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(() -> {
log.info("task:{}",index);
});
}
executorService.shutdown();
}
}
打印结果是有序打印的,因为只有一个线程
ScheduledThreadPool例子:
@Slf4j
public class ThreadPoolExample4 {
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
// executorService.schedule(() -> {
// log.warn("schedule run");
// }, 3, TimeUnit.SECONDS);
//延迟一秒,每隔三秒执行一次
executorService.scheduleAtFixedRate(() -> {
log.warn("schedule run");
}, 1, 3, TimeUnit.SECONDS);
// executorService.shutdown();
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
log.warn("timer run");
}
}, new Date(), 5 * 1000);
}
}
有定时器的功能,和Timer类相似。