1 前言
前面的几篇文章主要介绍了java中多线程编程中线程的一些概念。比如线程编程中一些常用的API,以及线程的暂停的恢复还有线程的终止。这些都是多线程编程的入门的基础,涉及到的都是单个线程使用,今天这篇文章就来说说在多线程编程中线程池的概念。
2 正文
先来说说为什么需要使用线程池?
“池”就是水池的意思,我们都知道水池就是用来存储水的,这样就避免我们每次需要用水的时候都需要重新打开水龙头或者去挑水,如果我们做饭的时候每次需要用水都需要打开水龙头或者去挑水,那么做一次饭的开销也太大了(当然开关水龙头的开销是不大的还很方便,这里就是做个比喻,但是一些没通自来水的地方真的是需要挑水,那这个开销还是很大的)。同理,线程池的使用可以:
1、减少创建和销毁线程的次数,每个工作线程都可以被重复使用,可以执行多个任务
2、可以根据系统的性能,调整线程池中工作线程的数量,防止因为消耗过度的内存而导致服务器崩了。
所以通过上面线程池可以带来的好处,我们可以得出线程池的作用:
线程池就是限制在系统中可以执行的线程的数量,根据系统的情况,自动或者手动的设置线程的数量,以达到系统运行的最佳效果。使用线程池控制工作线程的数量,多余的工作线程必须排队等候,一个任务执行完毕,再从队列中取组前面的任务开始执行,如果队列中没有等待的线程,那么线程池的资源就处于等待的状态。当一个新的任务需要运行的时候,如果线程池中有等待的工作线程,那么就可以开始运行,否则进入等候队列。
在java里面线程池的最顶部的接口是Executor接口,但是Executor并不是一个线程池,而是一个执行线程的工具而已。
在java中有几个比较重要的类:
1、ExecutorService:这是真正的Java提供的用于管理线程池的类。该类的两个作用:控制线程数量和重用线程
2、ScheduleExecutorService:用于解决那些需要任务重复执行的问题
3、ThreadPoolExecutor:ExecutorSrevice的默认实现
4、ScheduleThreadPoolExecutor:
继承ThreadPoolExecutor的ScheduledExecutorService接口的实现,周期性任务调试的类实现。
而在Executors类里提供了一些静态工厂,生成一些常用的线程池。具体常用的线程池实现如下:(返回值都是ExecutorService)
1、Executors.newCachedThreadPool():创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,就会回收部分空闲的线程(60秒没有执行的线程),当任务数量增加时,以可以智能的添加新的线程来处理任务。这个线程池不会对线程池的大小进行限制,线程池的大小完全依赖操作系统(JVM)能够创建的最大线程大小。
package com.jiangxia.chap5; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Executors.newCachedThreadPool */ public class Demo7newCachedThreadPool { public static void main(String[] args) { //创建一个可以缓存的线程池:newCachedThreadPool ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { try{ Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } //线程池运行 cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"正在被执行"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } } 复制代码
通过上面的运行结果可以发现这里的线程池为无限大,当执行当前任务时上一个任务已经完成,会复用执行上一个任务的线程,而不用每次新建线程。
2、Executors.newFixedThreadPool(int n):创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程池达到最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行而异常结束,那么线程池会补充一个新的线程。
package com.jiangxia.chap5; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * newFixedThreadPool: */ public class Demo8newFixedThreadPool { public static void main(String[] args) { ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { fixedThreadPool.execute(new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName()+"正在被执行"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } } 复制代码
这里设置的线程池的大小为5,并且休眠时间为2秒,所以每两秒打印5个线程信息。
3、Executors.newSginleThreadExecutor():创建一个单线程的线程池。这个线程池只有一个工作线程,也就是相当于单线程串行执行所有任务。如果 这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。线程池保证所有任务的执行顺序 会按照 任务的提交顺序执行。
package com.jiangxia.chap5; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * newSingleThreadExecutor */ public class Demo9NewSingleThreadPool { public static void main(String[] args) { ExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); for (int i = 0; i < 10; i++) { final int count = i; executorService.execute(new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName()+"正在被执行,count的值是:"+count); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } } 复制代码
4、Executors.newSingleThreadScheduledExecutor():创建一个单线程用于定时以及周期性执行任务的需求。
package com.jiangxia.chap5; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class Demo10newSingleThreadScheduledExecutor { public static void main(String[] args) { ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); System.out.println("准备执行:"+System.currentTimeMillis()); // 定时执行 // scheduleAtFixedRate(需要执行的线程, 第1个需要执行的线程延迟多长时间行, 两个线程间相隔的时间, 使用到时间单位) scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName()+"开始执行于"+System.currentTimeMillis()); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"执行结束于"+System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } },2,5,TimeUnit.SECONDS); } } 复制代码
5、Executors.newScheduledThreadPool(int n):创建定长的线程池。线程池运行定时以及周期性执行任务的需求。
package com.jiangxia.chap5; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * newScheduledThreadPool */ public class Demo11newScheduledThreadPool { public static void main(String[] args) { //创建一个定长线程池,支持定时及周期性任务执行——延迟执行 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); //延迟1秒执行 scheduledThreadPool.schedule(new Runnable() { public void run() { System.out.println("延迟1秒执行"); } }, 1, TimeUnit.SECONDS); //延迟1秒后每3秒执行一次 scheduledThreadPool.scheduleAtFixedRate(new Runnable() { public void run() { System.out.println("延迟1秒后每3秒执行一次"); } }, 1, 3, TimeUnit.SECONDS); } } 复制代码
3 总结
以上就是在java中如何创建线程池的几种方式,合理的使用线程池能够有助于提高系统的性能和效率。
如果觉得文章对你有帮助,就分享给更多的人吧!