线程池核心:三大方法,七大参数,四种拒绝策略
池化技术:
- 程序的运行的本质:占用系统的资源!优化资源的使用,可以考虑使用池化技术
- 池化技术:事先准备好一些资源,有人要用,就来这里拿,用完之后还给我
线程池的好处:
- 降低资源的消耗
- 提高响应速度
- 方便管理
线程的复用,可以控制最大并发数,管理线程
阿里巴巴java开发手册中对于线程池的生成是这样规定的:
一、创建线程池的三大方法
public class ExecutorDemo {
public static void main(String[] args) throws InterruptedException {
//单个线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
//创建一个固定大小的线程池
ExecutorService threadPool1 = Executors.newFixedThreadPool(5);
//可伸缩的线程池
ExecutorService threadPool2 = Executors.newCachedThreadPool();
System.out.println("单一的线程池");
for (int i = 0; i <10 ; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
TimeUnit.SECONDS.sleep(2);
System.out.println("固定大小的线程池");
for (int i = 0; i <10 ; i++) {
threadPool1.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
TimeUnit.SECONDS.sleep(2);
System.out.println("自动适应大小的线程池");
for (int i = 0; i <10 ; i++) {
threadPool2.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
}
}
结果如下,单一的线程池只有一个线程,固定大小的线程池的线程数是固定的,容量自适应的线程池并不意味着循环几次就会创建几个线程。
单一的线程池
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
固定大小的线程池
pool-2-thread-1 ok
pool-2-thread-2 ok
pool-2-thread-3 ok
pool-2-thread-4 ok
pool-2-thread-1 ok
pool-2-thread-1 ok
pool-2-thread-2 ok
pool-2-thread-4 ok
pool-2-thread-5 ok
pool-2-thread-1 ok
自动适应大小的线程池
pool-3-thread-1 ok
pool-3-thread-2 ok
pool-3-thread-3 ok
pool-3-thread-1 ok
pool-3-thread-2 ok
pool-3-thread-4 ok
pool-3-thread-6 ok
pool-3-thread-7 ok
pool-3-thread-5 ok
pool-3-thread-1 ok
二、线程池的七大参数
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<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;
}
三、四种拒绝策略
new ThreadPoolExecutor.AbortPolicy() //银行满了,还有人进来,不处理,抛出异常
new ThreadPoolExecutor.CallerRunsPolicy() //哪里来去哪里,一般为主线程main调用了线程池,所以回到主线程main处理
new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务
new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试和最早的线程去竞争,不会抛出异常
四、手动创建线程池
最大线程数该怎么定义
1、CPU密集型 电脑是几核,就是几,可以保持CPU得效率最高
2、IO密集型 判断程序中十分耗IO的线程,一般设置为它的两倍
int num = Runtime.getRuntime().availableProcessors();
System.out.println("电脑的核数:"+num);
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
//自定义线程池
//场景模拟,现在银行最大有五个柜台,平时只有两个在工作,这个为核心线程数,候客区有三把椅子,
// 这个是阻塞队列,如果候客区的三个位置满了,就会开启其他三个不在使用的窗口,如果其他三窗口
//也满了,同时候客区也满了,这时候还有人进来就会触发拒绝策略,根据不同的拒绝策略处理
ExecutorService myPool = new ThreadPoolExecutor(
2,
5,
1,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() //银行满了,还有人进来,不处理,抛出异常
);
try {
//最大承载: 阻塞队列+最大核心线程数
//当前可承载8个,但是9个不一定会抛出异常,可能之前的线程结束了,第九个线程刚好可以处理
//线程多了就会出现问题
for (int i = 1; i <=9 ; i++) {
myPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
myPool.shutdown();
}
}
}
运行结果:
1、第一种拒绝策略
java.util.concurrent.RejectedExecutionException: Task gdut.hzh.pool.ThreadPoolExecutorDemo$$Lambda$1/1831932724@7699a589 rejected from java.util.concurrent.ThreadPoolExecutor@58372a00[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 8]
at gdut.hzh.pool.ThreadPoolExecutorDemo.main(ThreadPoolExecutorDemo.java:30)
2、第二种拒绝策略
pool-1-thread-3 ok
pool-1-thread-3 ok
pool-1-thread-3 ok
pool-1-thread-3 ok
pool-1-thread-4 ok
main ok
pool-1-thread-1 ok
pool-1-thread-2 ok
pool-1-thread-5 ok