先说思想
池化技术是一种设计思想:
预先创建好资源并保留,避免资源的频繁创建 和 销毁 而浪费CPU资源。
常见场景:
- 线程池
- 对象池
- 连接池
线程池的三大方法
单一线程的线程池
public static void main(String[] args) {
//创建单一(大小)线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
try {
for(int i = 1;i <= 10; i++){
//利用线程池创建线程
singleThreadExecutor.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
} finally {
//关闭线程池
singleThreadExecutor.shutdown();
}
}
执行结果:
自定义线程池线程数
public static void main(String[] args) {
//创建大小为 5 的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
try {
for(int i = 1;i <= 10; i++){
//利用线程池创建线程
fixedThreadPool.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
} finally {
//关闭线程池
fixedThreadPool.shutdown();
}
}
执行结果:
线程池内线程数不限(只受设备影响)
public static void main(String[] args) {
//创建大小可变的线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
try {
for(int i = 1;i <= 10; i++){
//利用线程池创建线程
cachedThreadPool.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
} finally {
//关闭线程池
cachedThreadPool.shutdown();
}
}
执行结果:
三大方法源码审查
//单一
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) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
接着 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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
到这我们就能发现,其实开始说的线程池三大方法只是对 ThreadPoolExecutor 的进一步简单封装
简单的封装意义就不大了,虽然可以少写几行代码,但它让代码看起来不直观了,并且伴随着安全隐患。
所以我们要自己定义线程池,这样既直观又安全。
自定义线程池(推荐)
//自定义线程池 ThreadPoolExecutor,这样创建比较安全,并且容易阅读
/**最大线程数到底如何定义
* CPU 密集型: 逻辑处理核数 是几就是几
* 获得CPU核数
* System.out.println(Runtime.getRuntime().availableProcessors());
*
* IO 密集型: 最大线程数 一定要大于 项目中非常占资源的IO任务数
* (这样就能保证,在所有IO任务执行的时候,不会阻塞其他的进程执行。)
*/
//CPU密集型的写法(创建自定义线程池)
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,//正在营业的
Runtime.getRuntime().availableProcessors(),//最多营业人数
3,//3秒钟没人来,(5-2)这3个人就休息了
TimeUnit.SECONDS,//等待时间单位
new LinkedBlockingQueue<>(4),//候客区
Executors.defaultThreadFactory(),//线程创建工程
//new ThreadPoolExecutor.AbortPolicy()//默认拒绝策略:人满了(max_5 + queue_3 = 8_承载量)再来就抛出异常。
//new ThreadPoolExecutor.CallerRunsPolicy()//哪来的去哪里,(这里是从 main线程过来的,所以超出的进程会被打回main线程)
//new ThreadPoolExecutor.DiscardPolicy()//队列满了不会抛出异常,直接不管了(丢掉任务)
new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了不会抛出异常,但会尝试去和最早的线程竞争。
);
try {
for (int i = 1; i <= 5; i++) {
threadPoolExecutor.execute(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
}
} finally {
threadPoolExecutor.shutdown();
}