根据摩尔定律:集成电路上可容纳的晶体管数量每18个月翻一番,所以集成电路上的晶体管数量CPU会增加更多。但随着时间的推移,一块集成电路所能容纳的晶体管数量已经趋于饱和,摩尔定律也逐渐失效。因此,多核CPU逐渐成为主流,相应的多线程编程也开始流行和普及。当然,这是很久以前的事了。就目前而言,多线程编程已经成为程序员必备的职业技能。那么我们来看看多线程编程中最重要的话题“线程池”。什么是线程池?线程池(ThreadPool)是一种基于池化思想管理和使用线程的机制。它预先将多个线程存储在一个“池”中。当任务发生时,可以避免重新创建和销毁线程带来的性能开销。它只需要从“池”中取出相应的线程,就可以执行相应的任务。.池化的思想在计算机中也被广泛应用,例如:MemoryPooling:预先申请内存,提高内存申请速度,减少内存碎片。连接池:预先申请数据库连接,提高申请连接的速度,降低系统开销。对象池:回收对象以减少初始化和释放过程中代价高昂的资源损失。线程池的优势主要体现在以下四点:降低资源消耗:创建的线程通过池化技术得到复用,减少线程创建和销毁带来的损失。改进的响应能力:当任务到达时,它会立即执行,而无需等待线程创建。提高线程的可管理性:线程是稀缺资源。如果无限制地创建它们,不仅会消耗系统资源,还会因线程分配不合理导致资源调度不平衡,降低系统稳定性。使用线程池进行统一分配、调优和监控。提供更多更强大的功能:线程池是可扩展的,允许开发者为其添加更多的功能。例如延迟定时线程池ScheduledThreadPoolExecutor,可以让任务延迟或周期性执行。同时,阿里巴巴也在其《Java开发手册》中规定:线程资源必须通过线程池提供,不允许在应用程序中显式创建线程。说明:线程池的好处是减少创建和销毁线程所花费的时间和系统资源的开销,解决资源不足的问题。如果不使用线程池,可能会导致系统创建大量相同类型的线程,导致内存消耗或“过度切换”。了解了线程池是什么以及为什么要使用线程池之后,我们来看看如何使用线程池。使用线程池创建线程池的方式一共有7种,但是大体上可以分为2类:一类是通过ThreadPoolExecutor创建的线程池;另一个是通过Executors创建的线程池。创建线程池一共有7种方式(其中6种由Executors创建,1种由ThreadPoolExecutor创建):Executors.newFixedThreadPool:创建一个固定大小的线程池,可以控制并发线程数。线程将在队列中等待;Executors.newCachedThreadPool:创建一个可缓存的线程池。如果线程数超过处理要求,缓存会在一段时间后回收。如果线程数不够,则创建一个新线程;Executors.newSingleThreadExecutor:创建单线程Executors.newScheduledThreadPool:创建可以执行延迟任务的线程池;Executors.newSingleThreadScheduledExecutor:创建一个可以执行延迟任务的单线程线程池;Executors.newWorkStealingPool:创建线程池,用于抢占式执行(任务执行顺序不确定)【JDK1.8新增】。ThreadPoolExecutor:最原始的线程池创建方式,包含7个参数设置,后面会详细介绍。单线程池的含义从上面的代码我们可以看出newSingleThreadExecutor和newSingleThreadScheduledExecutor都创建了单线程池,那么单线程池是什么意思呢?答:虽然是单线程池,但是提供了工作队列和生命周期管理。工作线程维护等功能。然后我们来看一下每个线程池创建的具体使用。1.FixedThreadPool创建一个固定大小的线程池,可以控制并发线程数,超出的线程会在队列中等待。使用示例如下:publicstaticvoidfixedThreadPool(){//创建2个数据级线程池ExecutorServicethreadPool=Executors.newFixedThreadPool(2);//创建任务Runnablerunnable=newRunnable(){@Overridepublicvoidrun(){System.out.println("taskExecuted,thread:"+Thread.currentThread().getName());}};//线程池执行任务(一次添加4个任务)//执行任务有两种方式:submitandexecutethreadPool.submit(runnable);//执行方式一:submitthreadPool.execute(runnable);//执行方式二:executethreadPool.execute(runnable);threadPool.execute(runnable);}执行结果如下:如果觉得上面的方法比较麻烦,也可以使用更简单的使用方法如下代码所示:publicstaticvoidfixedThreadPool(){//创建线程池ExecutorServicethreadPool=Executors.newFixedThreadPool(2);//执行任务threadPool.execute(()->{System.out.println("TaskExecuted,thread:"+Thread.currentThread().getName());});}2.CachedThreadPool创建一个可缓存的线程池。如果线程数超过处理要求,缓存会在一段时间后回收。如果线程数不够,就新建一个线程。使用示例如下:publicstaticvoidcachedThreadPool(){//创建线程池ExecutorServicethreadPool=Executors.newCachedThreadPool();//执行任务for(inti=0;i<10;i++){threadPool.execute(()->{System.out.println("任务执行完毕,thread:"+Thread.currentThread().getName());try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedExceptione){}});}}执行结果如下:从上面的结果可以看出,线程池创建了10个线程来执行相应的任务。3.SingleThreadExecutor创建一个线程数单一的线程池,可以保证先进先出的执行顺序。使用示例如下:publicstaticvoidsingleThreadExecutor(){//创建线程池ExecutorServicethreadPool=Executors.newSingleThreadExecutor();//执行任务for(inti=0;i<10;i++){finalintindex=i;threadPool.execute(()->{System.out.println(index+":taskisexecuted");try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedExceptione){}});}}执行结果如下如下:4.ScheduledThreadPool为延迟任务创建可执行线程池。使用示例如下:publicstaticvoidscheduledThreadPool(){//创建线程池ScheduledExecutorServicethreadPool=Executors.newScheduledThreadPool(5);//添加定时执行任务(1s后执行)System.out.println("添加任务,time:"+newDate());threadPool.schedule(()->{System.out.println("任务执行完毕,时间:"+newDate());try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedExceptione){}},1,TimeUnit.SECONDS);}执行结果如下:从上面的结果我们可以看出任务是在1秒后执行的,符合我们的预期。5.SingleThreadScheduledExecutor创建一个单线程线程池,可以执行延时任务。使用示例如下:publicstaticvoidSingleThreadScheduledExecutor(){//创建线程池ScheduledExecutorServicethreadPool=Executors.newSingleThreadScheduledExecutor();//添加定时执行任务(2s后执行)System.out.println("添加任务,时间:"+newDate());threadPool.schedule(()->{System.out.println("任务执行完毕,时间:"+newDate());try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedExceptione){}},2,TimeUnit.SECONDS);}执行结果如下:从上面的结果我们可以看出任务是在2秒后执行的,符合我们的预期。6、newWorkStealingPool创建一个线程池,用于抢占式执行(任务执行顺序不定)。注意这个方法只能在JDK1.8+版本中使用。使用示例如下:publicstaticvoidworkStealingPool(){//创建线程池ExecutorServicethreadPool=Executors.newWorkStealingPool();//执行任务for(inti=0;i<10;i++){finalintindex=i;threadPool.execute(()->{System.out.println(index+"isexecuted,threadname:"+Thread.currentThread().getName());});}//确保任务执行完成while(!threadPool.isTerminated()){}}执行结果如下:从上面的结果可以看出任务的执行顺序是不确定的,因为它是抢占式执行的。7.ThreadPoolExecutor是最原始的线程池创建方式,包含7个参数设置。使用示例如下:publicstaticvoidmyThreadPoolExecutor(){//创建线程池ThreadPoolExecutorthreadPool=newThreadPoolExecutor(5,10,100,TimeUnit.SECONDS,newLinkedBlockingQueue<>(10));//执行任务for(inti=0;i<10;i++){finalintindex=i;threadPool.execute(()->{System.out.println(index+"执行,线程名:"+Thread.currentThread().getName());try{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}});}}执行结果如下:ThreadPoolExecutor参数介绍ThreadPoolExecutor最多可以设置7个参数,如下代码所示:publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueue
