线程池
ThreadPoolExecutor是线程池框架的一个核心类,线程池通过线程复用机制,并对线程进行统一管理
-降低系统资源消耗。通过复用已存在的线程,降低线程创建和销毁造成的消耗;
-提高响应速度。当有任务到达时,无需等待新线程的创建便能立即执行;
-提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗大量系统资源,还会降低系统的稳定性,使用线程池可以进行对线程进行统一的分配、调优和监控。
线程池的运行状态
线程池的运行状态总共有5种,其值和含义分别如下:
- RUNNING: 高3位为111,接受新任务并处理阻塞队列中的任务
- SHUTDOWN: 高3位为000,不接受新任务但会处理阻塞队列中的任务
- STOP: 高3位为001,不会接受新任务,也不会处理阻塞队列中的任务,并且中断正在运行的任务
- TIDYING: 高3位为010,所有任务都已终止,工作线程数量为0,线程池将转化到TIDYING状态,即将要执行terminated()结束钩子方法
- TERMINATED: 高3位为011,terminated()方法已经执行结束
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
构造器中各个参数的含义:
1.corePoolSize
线程池中的核心线程数。当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行。2.maximumPoolSize
线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize。3.keepAliveTime
线程空闲时的存活时间。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,keepAliveTime参数也会起作用,直到线程池中的线程数为0。4.unit
keepAliveTime参数的时间单位。 例如TimeUnit.DAYS5.workQueue
任务缓存队列,用来存放等待执行的任务。如果当前线程数为corePoolSize,继续提交的任务就会被保存到任务缓存队列中,等待被执行。一般来说,这里的BlockingQueue阻塞队列有以下三种选择:
- SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。因此,如果线程池中始终没有空闲线程(任务提交的平均速度快于被处理的速度),可能出现无限制的线程增长。
- LinkedBlockingQueue:基于链表结构的阻塞队列,如果不设置初始化容量,其容量Integer.MAX_VALUE,即为无界队列。因此,如果线程池中线程数达到了corePoolSize,且始终没有空闲线程(任务提交的平均速度快于被处理的速度),任务缓存队列可能出现无限制的增长。
- ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务。6.threadFactory
线程工厂,创建新线程时使用的线程工厂。7.handler
任务拒绝策略,当阻塞队列满了,且线程池中的线程数达到maximumPoolSize,如果继续提交任务,就会采取任务拒绝策略处理该任务,线程池提供了4种任务拒绝策略:
- AbortPolicy:丢弃任务并抛出RejectedExecutionException异常,默认策略;
- CallerRunsPolicy:由调用execute方法的线程执行该任务;
- DiscardPolicy:丢弃任务,但是不抛出异常;
- DiscardOldestPolicy:丢弃阻塞队列最前面的任务,然后重新尝试执行任务(重复此过程)。
当然也可以根据应用场景实现RejectedExecutionHandler接口自定义饱和策略,如记录日志或持久化存储不能处理的任务。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
// ThreadPoolExecutor pool=new ThreadPoolExecutor(3, 9, 16, TimeUnit.DAYS,new LinkedBlockingDeque<>());
// ThreadPoolExecutor pool=new ThreadPoolExecutor(3, 9, 16, TimeUnit.DAYS,new SynchronousQueue<>());
// ThreadPoolExecutor pool=new ThreadPoolExecutor(3, 9, 16, TimeUnit.DAYS,new ArrayBlockingQueue<>(2));
ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 6, 1, TimeUnit.HOURS, new SynchronousQueue<>(),
Executors.defaultThreadFactory(), new CallerRunsPolicy());
for (int i = 0; i < 10; i++) {
int kk = i;
pool.submit(() -> {
for (int k = 0; k < 5; k++) {
// System.out.println(Thread.currentThread());
// System.err.println(Thread.currentThread());
System.out.println(Thread.currentThread() + "::" + kk);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
pool.shutdown();
}
}
Executors创建线程池
Executors创建线程池
- newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收重用时则新建线程
new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
用来创建一个可以无限扩大的线程池,适用于服务器负载较轻,执行很多短期异步任务
Integer.MAX_VALUE--OOM
- newFixedThreadPool 创建一个固定大小的定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于可以预测线程数量的业务中,或者服务器负载较重,对当前线程数量进行限制
- newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
- newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO先进先出, LIFO后进先出, 优先级)执行适用于需要保证顺序执行各个任务,并且在任意时间点,不会有多个线程是活动的场景
- newWorkStealingPool:创建一个拥有多个任务队列的线程池,可以减少连接数创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test2 {
public static void main(String[] args) {
// ExecutorService es=Executors.newCachedThreadPool();
// ExecutorService es=Executors.newFixedThreadPool(3);
ExecutorService es = Executors.newSingleThreadExecutor();
for(int i=0;i<1000;i++) {
int kk=i;
es.submit(()->{
for(int k=0;k<5;k++) {
System.out.println(Thread.currentThread()+"::"+kk);
}
});
}
es.shutdown();
}
}
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Test3 {
public static void main(String[] args) {
// ExecutorService es = Executors.newScheduledThreadPool(3);
// for (int k = 0; k < 10; k++) {
// es.submit(() -> {
// for (int i = 0; i < 5; i++) {
// System.out.println(Thread.currentThread());
// }
// });
// }
// es.shutdown();
ScheduledExecutorService ses=Executors.newScheduledThreadPool(3);
System.out.println(new Date().getTime());
//延迟指定时间后开始执行
// ses.schedule(()->{
// for(int i=0;i<10;i++) {
// System.out.println(Thread.currentThread()+"::"+new Date().getTime());
// }
// }, 100, TimeUnit.MILLISECONDS);//计划100ms后开始执行Runnable
// ses.shutdown();
//延迟100ms后开始执行Runnable,以后每隔50ms执行一次
ses.scheduleAtFixedRate(()->{
for(int i=0;i<10;i++) {
System.out.println(Thread.currentThread()+"::"+new Date().getTime());
}
}, 100, 50, TimeUnit.MILLISECONDS);
}
}
ExecutorService提供提交任务的方式
ExecutorService提供提交任务的方式
线程池框架提供了两种方式提交任务,submit()和execute()通过submit()方法提交的任务可以返回任务执行的结果,通过execute()方法提交的任务不能获取任务执行的结果。
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class Test4 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Future[] fs=new Future[10];
ExecutorService es=Executors.newFixedThreadPool(3);
// es.execute(null);//这里的参数为Runnable,所以不会有返回值
for(int i=1;i<=10;i++) {
int begin=(i-1)*100+1;
int end=i*100;
fs[i-1]=es.submit(()->{ // 参数可以是Callable或者Runnable,如果参数是Runnable,则没有返回值;如果参数是Callable,则可以通过Future获取返回值
int res=0;
for(int k=begin;k<=end;k++) {
res+=k;
}
return res;
});
}
int res=0;
for(Future<Integer> f:fs)
res+=f.get();
System.out.println(res);
}
}
ExecutorService的方法:关闭线程池
ExecutorService的方法:关闭线程池
shutdownNow:对正在执行的任务全部发出interrupt(),停止执行,对还未开始执行的任务全部取消,并且返回还没开始的任务列表
shutdown:当调用shutdown后,线程池将不再接受新的任务,但也不会去强制终止已经提交或者正在执行中的任务
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test5 {
public static void main(String[] args) throws Exception {
// ExecutorService es = Executors.newFixedThreadPool(2);
// es.submit(()->{ //Runnable
// for(int i=0;i<10;i++){
// System.out.println("Hello "+i);
// try {
// Thread.sleep(200);
// } catch (InterruptedException e) {
// e.printStackTrace();
// break;
// }
// }
// });
// System.out.println("main......");
// es.shutdown();
// es.shutdownNow();
ExecutorService es = Executors.newFixedThreadPool(2);
Future f = es.submit(() -> { //Callable<V>
for (int i = 0; i < 10; i++) {
System.out.println("Hello " + i);
Thread.sleep(200); }
return 100;
});
Object obj=f.get(); //阻塞当前main线程
System.out.println("main......");
// es.shutdown();
es.shutdownNow();
}
}