Java线程池(原理与代码实现)-小白入门
作为一位程序员,多线程开发是必不可少的,对线程池也或多或少的有所了解,或者只是会用,对其原理却朦朦胧胧。
多线程技术主要解决处理器单元内多个线程执行的问题,可以显著减少处理器单元的闲置时间,增加处理器但愿的吞吐能力。
线程池是什么?
线程池由任务队列和工作线程组成,它可以重用线程来避免线程创建的开销,在任务过多时通过排队避免创建过多线程来减少资源的消耗与竞争,确保任务有序完成。简而言之,线程池就是事先将线程放到一个容器中,当使用线程的时候,不再去new出一个线程,而是直接从线程池取出来就可以了。
线程池的好处:
1.降低资源消耗:重复利用线程池中的线程节省线程创建与销毁带来的消耗;
2.提高性能:当任务需求时,可以不用创建线程直接执行,主要是直接从线程池中取出线程去执行
3.提高线程的可管理性:线程是稀缺资源,而且也是任务中不可缺少的资源,如果频繁的且无节制的创建会消耗系统资源,降低系统稳定性而导致系统崩溃,内存溢出等问题。
线程池如何使用
java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池
创建线程池的方式有以下四种:
- ExecutorService executors = Executors.newFixexThreadPool(n);创建固定大小的线程池,每提交一个任务就是一个线程,直到达到线程池的最大数量,然后进入等待队列直到前面的任务完成才能继续执行
- ExecutorService executors = Executors.newCachedThreadPool();创建可缓存的线程池,根据需要创建线程池,上限为操作系统的内存条大小【推荐使用】
- ExecutorService executors = Executors.newSingleThreadExecutor();创建单个线程的线程池,即单线程串行执行任务
- ScheduleExecutorService executors = Executors.newScheduledThreadPool();创建调度线程池,没有大小限制,若使用不当,可能会出现内存溢出,程序中断等情况。
使用上述创建方法创建后,通过execute()方法启动线程池。
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具,真正的线程池接口是ExecutorService。
比较重要的几个类:
ExecutorService | 真正的线程池接口 |
---|---|
ThreadPoolExecutor | ExecutorService的默认实现。 |
ScheduledExecutorService | 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。 |
ScheduledThreadPoolExecutor | 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。 |
ThreadPoolExecutor详解
ThreadPoolExecutor的完整构造方法为:
//有多个构造方法,最终都指向这个最多参数的构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
corePoolSize:池中所保存的线程数,包括空闲线程;
maximumPoolSize:池中允许的最大线程数;
keepAliveTime:默认是 0,当线程没有任务处理后空闲线程保持多长时间,不推荐使用;
unit:keepAliveTime 参数的时间单位;
workQueue:任务等待队列,当达到 corePoolSize的时候就向该等待队列放入线程信息(默认为一个LinkedBlockingQueue);
threadFactory:是构造 Thread 的方法,一个接口类,可以使用默认的 default实现,也可以自己去包装和传递,主要实现 newThread 方法即可。即执行程序创建新线程时使用的工厂。
defaultHandler:当参数 maximumPoolSize 达到后丢弃处理的方法实现,java 提供了 5种丢弃处理的方法,当然也可以自己弄,主要是要实现接口 RejectedExecutionHandler 中rejectedExecution(Runnabler, ThreadPoolExecutor e) 方法,java 默认使用的是AbortPolicy,他的作用是当出现这种情况的时候抛出一个异常;通常得到线程池后会调用其中的 submit 或 execute 方法去提交执行异步任务,其实 submit 方法最终会调用execute 方法来进行操作,只是他提供了一个 Future来托管返回值的处理而已,当你调用需要有返回值的信息时用它来处理是比较好的,这个 Future 会包装 Callable 信息。
代码实现
package threadTest;
import java.util.concurrent.*;
public class ThreadPoolTest {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(4, Math.min(CPU_COUNT - 1, 5));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 2;
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>(10);
public static void main(String[] args) {
System.out.println("核心线程数=" + CORE_POOL_SIZE);
System.out.println("最大线程数=" + MAXIMUM_POOL_SIZE);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, //核心线程数
MAXIMUM_POOL_SIZE, //线程池中最大的线程数
60, //线程的存活时间,没事干的时候,空闲的时间
TimeUnit.SECONDS, //线程存活时间的单位
sPoolWorkQueue, //线程缓存队列
new ThreadFactory() { //线程创建工厂,如果线程池需要创建线程会调用newThread来创建
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setDaemon(false);
return thread;
}
});
threadPoolExecutor.allowCoreThreadTimeOut(true);
for (int i = 0; i < 20; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行完毕" + Thread.currentThread().getName());
}
};
//丢给线程池去执行
threadPoolExecutor.execute(runnable);
}
}
}
结果显示:
参考代码:https://www.imooc.com/article/51147