在了解为什么使用线程池之前我们先回顾下Java创建线程的几种方式。
第一种,继承Thread类
/**
* 线程使用方法一:继承Thread类重写run方法,通过start方法开始调用
*/
public static class Thread01 extends Thread{
@Override
public void run() {
System.out.println("Thread01线程开始执行...");
int res = 10 / 2;
System.out.println("Thread01线程执行完毕,结果:" + res);
}
}
第二种,实现Runnable接口
/**
* 线程用法二:实现Runnable接口,通过new Thread时传入
*/
public static class Runnable01 implements Runnable{
@Override
public void run() {
System.out.println("Runnable01线程开始执行...");
int res = 10 / 2;
System.out.println("Runnable01线程执行完毕,结果:" + res);
}
}
第三种,使用Callable接口
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main....start");
// Callable
FutureTask futureTask = new FutureTask<>(new Callable01());
new Thread(futureTask).start();
// 此时没有体现出Callable最大的好处,主线程main还是提前与异步线程,于是我们可以通过
// get方法,阻塞线程实现获取返回值
Integer integer = (Integer) futureTask.get();
System.out.println("main...end:" + integer);
}
/**
* 线程使用方法三:使用Callable接口,Callable接口与Thread和Runnable接口不同的是
* Callable接口拥有返回值,可与FutureTask实现异步获取返回值
*/
public static class Callable01 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("Callable01线程开始执行...");
int res = 10 / 2;
System.out.println("Callable01线程执行完毕,结果:" + res);
return res;
}
}
但是无论使用哪种方式创建线程,在我们的业务系统中如此操作必然会造成系统资源难以管理,于是线程池的概念就应运而生,在庞大的业务系统中,任何的线程操作都应该交由线程池来管理,大家都应该把自己的任务交给线程池,由线程池来代理运行。
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main....start");
// 在业务代码中,随时创建线程是大忌,我们可以采用线程池的方式来管理线程
// 创建一个有10个线程的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
// 分配一个任务,由其执行
service.execute(new Runnable01());
System.out.println("main...end:");
}
这种方式则是使用了Executors工具类帮我们创建线程池,我们可以看下如何使用原生的方法去创建线程池。
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main....start");
/**
* 最多拥有七个参数:
* corePoolSize:核心线程数,指线程池创建完成后系统中就已经存在的线程数,就等待来接收异步任务
* maximumPoolSize:最多存在的线程数,用于控制资源并发
* keepAliveTime:当线程数量大于核心数量,空闲线程等待接收新任务的时间,超时即销毁
* 需要释放的线程数=maximumPoolSize-maximumPoolSize
* unit:时间单位
* BlockingQueue<Runnable> workQueue:阻塞队列,如果任务有很多无法处理,多余任务将会在此等待处理。
* threadFactory:线程工厂,一般不用改
* RejectedExecutionHandler handler:如果队列满了,按照我们指定的策略拒绝执行任务。
*/
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(10000), // 指定最多的等待任务,超出即拒绝执行
Executors.defaultThreadFactory(), // 使用默认的线程工厂,此参数可省略
new ThreadPoolExecutor.AbortPolicy()
);
System.out.println("main...end:");
但是以上都可以使用Executors工具类来实现创建:
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main....start");
// 缓存线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 创建固定线程数的线程池
ExecutorService executorService = Executors.newFixedThreadPool(200);
// 创建支持任务调度的线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(100);
// 创建单线程线程池,从队列里面获取任务,依次执行
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
System.out.println("main...end:");
}