1.手动创建线程池(7个参数)
//juc提供了4个重载方法
//分别需要5个参数 , 6个参数5+a , 6个参数5+b , 7个参数
new ThreadPoolExecutor( ... );
int corePoolSize : 核心线程数 , 池内线程数的下限,未达下限,优先创建新线程。达到下限,任务装入队列
int maximumPoolSize : 最大线程数。队列满,创建更多线程,直到达到上限。拒绝任务
long keepAliveTime : 线程空闲存活时间(数字)
TimeUnit unit : 配合keepAliveTime , 指定存活时间(单位)
BlockingQueue<Runnable> workQueue : 任务等待队列(阻塞队列)
ArrayBlockingQueue ,底层数组,适合指定长度
LinkedBlockingQueue,底层链表,适合无限长度
DelayQueue , 适合延迟(定时)任务
PriorityBlockingQueue 自认队列(元素自然有序,大小)
SynchronousQueue 同步队列,只能存一个元素
ThreadFactory threadFactory : 线程工厂,在线程池内部用来创建线程的。可以自定义
RejectedExecutionHandler handler : 拒绝策略,当任务队列满,线程达到上下且都忙碌,新任务被拒绝
juc提供了多种拒绝策略,也可以自定义
AbortPolicy 拒绝任务,并抛出异常
DiscardPolicy 拒绝任务,没有任何提示
DiscardOldestPolicy 拒绝最终加入等待队列的任务,
新任务加入队列
CallerRunsPolicy 拒绝任务,将任务交还给发起任务的线程
2.使用线程池(常用方法)
pool.getPoolSize();//获得线程数
pool.prestartAllCoreThreads() ;//预先创建核心数量的线程
pool.allowCoreThreadTimeOut(true);//设置核心数量的线程也可以超时被销毁
Queue q = pool.getQueue();//获得任务队列
q.size(); //等待执行的任务数量
q.clear();
q.add();
q.remove();
pool.submit(Runnable); //获得Future对象,可以知道线程是否执行完毕,但无法获得返回值
pool.submit(Callable); //获得Future对象,可以知道线程是否执行完毕,也可以获得返回值
future.isDone();//快速获得boolean结果,用来判断任务是否执行完毕
future.get();//获得任务执行的结果,如果还没有执行完毕,当前线程会处于等待状态
pool.execute(Runnable); //执行Runnable任务
3.自定义线程工厂
-
当需要自定义线程的名字,线程的优先级,精灵线程状态时,需要自定义线程工厂。
-
如何自定义线程工厂
-
自定义工厂类,实现ThreadFactory接口,重写方法
newThread()
-
在创建线程池对象时,传递上述线程工厂对象
-
public class Test5 {
public static void main(String[] args) {
System.out.println("------------------------");
ThreadPoolExecutor pool = new ThreadPoolExecutor(
5,15,
5, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5),
new MyThreadFactory());
pool.execute(()->{
System.out.println("---->"+Thread.currentThread().getName());
});
}
}
class MyThreadFactory implements ThreadFactory{
//int count = 1 ;
AtomicInteger count = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("pool-a-thread-"+count.getAndIncrement());
t.setPriority(8);
//t.setDaemon(true);
return t;
}
}
4.自定义拒绝策略
实现,当前线程池能力不足时,交给另一个线程池来执行。
-
自定义拒绝策略类,
-
创建线程池对象时,传递自定义拒绝策略对象
5.自动获得线程池
//适合任务量大,功能简单的任务,需要确保每一次任务都需要快速反应
ExecutorService pool = Executors.newCachedThreadPool();
//线程池中只有一个线程,确保任务顺序执行
//当内部的线程执行任务出错时,会创建一个新线程。
ExecutorService pool = Executors.newSingleThreadExecutor();
//线程池内装载固定数量的线程
ExecutorService pool = Executors.newFixedThreadPool(5);
//可以执行定时任务的线程池
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
//工作窃取线程池
//1. 本质是ForkJoinPool
//2. 提供了2个重载构造器,一个传参指定内部线程的数量,第二个不传参默认是逻辑处理器的数量
//3. 工作窃取线程池的机制
// 所有的线程都是精灵线程
// 之前的线程池,无论有多少个线程,都只有一个任务队列
// 当一瞬间有多个线程同时完成任务时,就会同时去队列中获得新任务,此时就会发生阻塞(性能低)
// 工作窃取线程池内部,每一个线程都配有一个任务队列,当完成任务时,去自己的队列中获取新任务
// 当自己的队列中的任务完成后,就去其他线程任务队列中"窃取"任务执行
// 每一个队列都是双端队列,当前线程从a端获取任务,其他线程从b端窃取任务
ExecutorService pool = Executors.newWorkStealingPool();
//延迟指定时间执行
//
pool.schedule(()->{
System.out.println("-----------------------------");
},5, TimeUnit.SECONDS);
//延迟指定时间,开始执行
//执行后,每隔指定时间,重复执行
//每次执行的时间 会与 间隔时间叠加
pool.scheduleAtFixedRate(
()->{
for(int i = 1 ;i<=2;i++){
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},5,2,TimeUnit.SECONDS);
//延迟指定时间,开始执行
//执行后,每隔指定时间,重复执行
//每次执行的时间 不会与 间隔时间叠加
pool.scheduleWithFixedDelay(
()->{
for(int i = 1 ;i<=2;i++){
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},5,2,TimeUnit.SECONDS);
6.线程池生命周期
-
运行状态 RUNNING
状态切换:随着线程池对象的创建,就立刻进入运行状态
状态效果:运行时的线程池,可以接收任务,处理任务,缓存任务
-
关闭状态 SHUTDOWN
状态切换:在运行状态时,调用
pool.shutdown()
方法状态效果:线程池不再接收新任务,但会执行完先有的任务及队列中的任务
-
停止状态 STOP
状态切换:运行时直接调用
pool.shutdownNow()
方法或处于shutdown状态的线程池完成了所有手头任务,自动进入stop状态
状态效果:线程池不再接收新任务,清除队列中的任务,中断正在执行的任务
-
整理状态 TIDYING
状态切换:没有方法能直接切换到整理状态
处于stop状态的线程池,完成了所有任务处理工作,自动调用terminated()
状态效果:逻辑上就是整理,释放资源,类似于finally中做的事
-
终结状态 TERMINATED
状态切换:没有方法能直接切换到终结状态
处于整理状态的线程池,执行完terminated方法,自动进入终结状态
状态效果:完全关闭线程池
线程池5种状态及线程数量的源码表示
-
使用一个变量值表示线程池的状态及线程数量
-
将一个int类型的值,按照其bit结构处理 , 拥有4 * 8 = 32 个bit
-
使用高3位的bit,表示状态
-
使用后面29位的bit,表示数量