什么是线程池
java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池,通俗来说,线程池是为了避免系统频繁的创建和销毁线程,使创建出来的线程可以进行复用的一个保存线程的容器,当线程工作完成,并不直接销毁,而是放入池中等待复用
常见的线程池
本文着重介绍Executor框架中的5中常用线程池
- newFixedThreadPool()
- newSingleThreadPool()
- newCachedThreadPool()
- newSingleThreadScheduledExecutor()
- newScheduledThreadPool()
上述5个是Executor中具有不同特性的线程池:
newFixedThreadPool():这是一个返回固定线程数量的线程池。当任务数超过设定的值时,会进入任务队列中等待出现空闲线程时在执行。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
nThreads 表示需要设定的线程数。
newSingleThreadPool():该方法返回的是只有一个线程的线程池。
newCachedThreadPool():该方法返回的是一个动态的线程池,如池内有线程可以使用则进行复用,当新任务进入时并没有可用线程,则会创建新的线程处理任务,当全部完成后返回线程池等待复用。
newSingleThreadScheduledExecutor():和newScheduledThreadPool() 这两个方法均返回一个ScheduledExecutorService对象,区别在于newSingleThreadScheduledExecutor返回的是只有1个线程数的线程池,这两个方法都扩展了在给定时间执行任务的功能,如进行延迟执行,或者周期性执行某些任务。
在ScheduledExecutorService类中有如下方法:
public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
该方法为在给定时间后,执行该任务(一次执行)
command:需要执行的线程。
delay:指定时间
unit: 时间单位(时分秒等)
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
command:需要执行的线程。
initialDelay:设置第一次延后时间执行。
period:周期时间
unit:时间类型。
这两者均为周期性执行,区别在于前者不会因为程序执行的时间而延后,后者是等待改任务执行之后在计算周期时间。 (通俗的讲。例如该任务2秒执行完成,period设置为3秒,则下次执行的时间为3秒后,而scheduleWithFixedDelaydelay设置为3秒,则下次执行时间为2+3=5秒后执行)
值得注意的是如果程序执行时间大于调度时间,则scheduleAtFixedRate会在程序执行完后立即执行下一次调度。(读源码了解掉这两个方法都是在任务执行完成后再去验证执行时间和调度时间),所以当执行时间小于period时间时会按照period设置的周期执行,如果大于则任务执行完立即调用。
拒绝策略
上面所讲的开启线程的方法本质上都是对ThreadPoolExecutor的封装。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
其中RejectedExecutionHandler 就是本节所说的拒绝策略。
当任务源源不断的过来,而我们的系统又处理不过来的时候,我们要采取的策略是拒绝服务,RejectedExecutionHandler中介绍了四种策略:
1、直接丢弃(DiscardPolicy)
2、丢弃队列中最老的任务(DiscardOldestPolicy)。
3、抛异常(AbortPolicy)
4、将任务分给调用线程来执行(CallerRunsPolicy)。
当然我们也可以自定义拒绝策略,例:
public void run() {
System.out.println(System.currentTimeMillis() + ":Thread ID:" + Thread.currentThread().getId());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]) throws InterruptedException {
MyTask myTask = new MyTask();
ExecutorService executorService = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(10), Executors.defaultThreadFactory()
, new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(r.toString() + " is discard");
}
});
for (int i = 0; i < 100; i++) {
executorService.submit(myTask);
Thread.sleep(10);
}
}
其中
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(r.toString() + " is discard");
}
中我们可以自行设置拒绝方法。
本文深入探讨Java中线程池的原理与应用,包括固定线程池、单一线程池、缓存线程池及定时线程池的特性与区别。同时,解析线程池的拒绝策略,帮助读者理解如何在高并发场景下合理配置线程池。
1147

被折叠的 条评论
为什么被折叠?



