为什么用线程池,优势。
线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
它的主要特点为:线程复用;控制最大并发数;管理线程。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
线程池的使用
java中的线程池是通过Executor框架实现的,该框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor这几个类。
创建线程的第四种方法就是通过线程池
- Executors.newFixedThreadPool(int):固定数量的线程池。
- Executors.newSingleThreadExecutor():一个线程的线程池。
- Executors.newCachedThreadPool():数量不固定的线程池。
Executors.newFixedThreadPool(int)
package juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
//模拟10个用户办理业务,每个用户就是一个来自外部的请求线程
try{
for (int i = 1; i <= 10 ; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 办理业务");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
可以看到,10个资源只能由线程池当中的5个线程来处理。
Executors.newSingleThreadExecutor()
package juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
//模拟10个用户办理业务,每个用户就是一个来自外部的请求线程
try{
for (int i = 1; i <= 10 ; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 办理业务");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
一池一线程。只由1个线程处理。
Executors.newCachedThreadPool()
package juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
//模拟10个用户办理业务,每个用户就是一个来自外部的请求线程
try{
for (int i = 1; i <= 10 ; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 办理业务");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
由多少个线程处理不一定。
源码
newCachedThreadPool的核心线程数是0,最大线程数是Integer的最大值。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
newFixedThreadPool的核心线程数和最大线程数都是传的参数。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
newSingleThreadExecutor的核心线程数和最大线程数为1。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
可以看到,以上三种的底层源码都是通过 ThreadPoolExecutor来实现的。
线程池七大参数。
继续翻源码,可以看到线程池其实是有起个参数的。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- corePoolSize:线程池中的常驻核心线程数。
- maximumPoolSize:线程池能够容纳同时执行的最大线程数。此值必须大于1。
- keepAliveTime:多余的空闲线程的存活时间。当前线程池数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余空闲线程会被销毁直到只剩下corePoolSize个线程为止。
- unit:keepAliveTime的单位。
- workQueue:任务队列,被提交但尚未被执行的任务。
- threadFactory:表示生产线程池中工作线程的线程工厂,用于创建线程,一般用默认即可。
- handler:拒绝策略,表示当队列满了并且工作线程大于等于最大线程数是如何拒绝多余的资源。
线程池大致实现流程:
当任务进入线程池时,核心线程会对其进行处理;当核心线程满了,此时如果还有任务,这些任务会进入到阻塞队列中,如果队列中的任务满了,线程池就会扩容出其他线程,最多是maximumPoolSize个,多出的线程继续对队列中的任务进行处理。如果最大线程数也满了,队列中的任务也满了,这是如果还有任务进来,就会触发拒绝策略。渐渐的任务数下降了,多出核心线程数的其他线程空闲了,会存活一段时间,这个时间是由keepAliveTime决定的,超出这个时间就会被销毁,直到等于核心线程数。
线程池拒绝策略
当线程池中等待队列已经满了,并且数量已经达到最大线程数了,这时无法为其他任务服务,需要启动拒绝策略。
- AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行。
public class MyThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
1L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//模拟10个用户办理业务,每个用户就是一个来自外部的请求线程
try{
for (int i = 1; i <= 9 ; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 办理业务");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
- CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。
public class MyThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
1L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
//模拟10个用户办理业务,每个用户就是一个来自外部的请求线程
try{
for (int i = 1; i <= 10 ; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 办理业务");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
- DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务。
public class MyThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
1L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
//模拟10个用户办理业务,每个用户就是一个来自外部的请求线程
try{
for (int i = 1; i <= 10 ; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 办理业务");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
- DiscardPolicy:直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案。
public class MyThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
1L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());
//模拟10个用户办理业务,每个用户就是一个来自外部的请求线程
try{
for (int i = 1; i <= 10 ; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 办理业务");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}