多线程之线程池的应用
来源:互联网 发布:maka软件下载 编辑:程序博客网 时间:2024/05/16 14:44
我们在开发的过程中经常会遇到多线程问题,多线程在整个java基础也是计较重要的知识点。今天跟大家分享一个我在写程序过程中遇到的线程池的简单应用。
- 线程池
- 如何使用
- 一创建线程池的方法
- 二创建任务
- 三执行任务
- 四关闭线程池
- 五案例分享
在Java1.5中提供了一个非常高效实用的多线程包:java.util.concurrent,提供了大量高级工具,可以帮助开发者编写高效易维护、结构清晰的Java多线程程序。
线程池?
之前我们在使用多线程都是用Thread的start()来创建启动一个线程,但是在实际开发中,如果每个请求到达就创建一个新线程,开销是相当大的。服务器在创建和销毁线程上花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。这就引入了线程池概念。
线程池的原理其实就是对多线程的一个管理(可以抽象的理解为原来是一个工厂来订单了,老板就聘请一个工人(线程)干活,干完活就把他炒鱿鱼了,这样每次来订单都得聘请一个工人,且老板得不停去聘请工人,并分配任务,现在老板就请了一个主管(线程池),并和工人签订长期合同,这样每次有订单(任务)来了,直接交给主管调度工人去干活),为了实现异步机制的一种方法,其实就是多个线程执行多个任务,最终这些线程通过线程池进行管理…不用手动去维护…一次可以处理多个任务,这样就可以迅速的进行响应…
在java.util.concurrent包下,提供了一系列与线程池相关的类。合理的使用线程池,可以带来多个好处:
- (1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗;
- (2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行;
- (3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
- (4)线程池可以应对突然大爆发量的访问,通过有限个固定线程为大量的操作服务,减少创建和销毁线程所需的时间。
如何使用?
1.创建线程池2.创建任务3.执行任务4.关闭线程池
一、创建线程池的方法
我们一般通过工具类Executors的静态方法来获取线程池或静态方法。
介绍四种常用创建方法
1. ExecutorService executorService1 = Executors.newSingleThreadExecutor();
单例线程,表示在任意的时间段内,线程池中只有一个线程在工作…
2. ExecutorService executorService2 = Executors.newFixedThreadPool(10);
固定线程池,表示创建一个可复用的固定线程池,线程池中最大线程个数为10,当有任务被提交时,创建一个线程去执行,直到达到当线程池中最大个数为止。当提交的任务个数大于线程池中最大线程个数时,则存入队列中,等待执行。如果某个线程在执行期间发生了异常而终止,会补充一个新线程去执行余下。
3. ExecutorService executorService3 = Executors.newScheduledThreadPool();
调度型线程池,一个大小无限的线程池,线程池会根据Scheduled(任务列表)进行延迟执行,或者是进行周期性的执行.适用于一些周期性的工作.支持定时以及周期性执行任务的需求任务。
4. ExecutorService executorService4 = Executors.newCacheThreadPool();
缓存线程池,无固定大小缓存线程池,先查看线程池中是否有当前执行线程的缓存,如果有就resue(复用),如果没有,那么需要创建一个线程来完成当前的调用.并且这类线程池只能完成一些生存期很短的一些任务.并且这类线程池内部规定能resue(复用)的线程,空闲的时间不能超过60s,一旦超过了60s,就会被移出线程池.非常灵活
例子
public class Executor { public static void main(String[] args) { //定义了线程池中最大存在的线程数目 ExecutorService executorService=Executors.newFixedThreadPool(10); //添加了一个任务... executorService.execute(new Runnable() { @Override public void run() { while(true){ System.out.println("begincode"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }); }}
执行结果就是无限循环每隔1秒打印一个begincode…
二、创建任务
任务分为两种一种是有返回值的,一种是没有返回值的
- 无返回值的任务就是一个实现了runnable接口的类.使用run方法
- 有返回值的任务是一个实现了callable接口的类.使用call方法
public class Begincode implements Runnable { public void run() { System.out.println(“Begincode--runable”); } } public class Begincode implements callable{ public String call() { System.out.println(“Begincode--callable”); return "Begincode"; }}
三、执行任务
通过java.util.concurrent.ExecutorService接口对象来执行任务,该对象有两个方法可以执行任务execute和submit
execute这种方式提交没有返回值,也就不能判断是否执行成功。
submit这种方式它会返回一个Future对象,通过future的get方法来获取返回值,get方法会阻塞住直到任务完成。
四、关闭线程池
当我们不需要使用线程池的时候,我们需要对其进行关闭…有两种方法可以关闭掉线程池…
shutdown()
- shutdown并不是直接关闭线程池,而是不再接受新的任务…如果线程池内有任务,那么把这些任务执行完毕后,关闭线程池….
shutdownNow()
- shutdownNow这个方法表示不再接受新的任务,并把任务队列中的任务直接移出掉,如果有正在执行的,尝试进行停止…
五、案例分享
场景:从数据库中获取url,并利用httpclient循环访问url地址,并对返回结果进行操作
分析:由于是循环的对多个url进行访问并获取数据,为了执行的效率,考虑使用多线程,url数量未知如果每个任务都创建一个线程将消耗大量的系统资源,最后决定使用线程池。
public class GetMonitorDataService { private Logger logger = LoggerFactory.getLogger(GetMonitorDataService.class); @Resource private MonitorProjectUrlMapper groupUrlMapper; @Resource private MonitorDetailBatchInsertMapper monitorDetailBatchInsertMapper; public void sendData(){ //调用dao查询所有url MonitorProjectUrlExample example=new MonitorProjectUrlExample(); List<MonitorProjectUrl> list=groupUrlMapper.selectByExample(example); logger.info("此次查询数据库中监控url个数为"+list.size()); //获取系统处理器个数,作为线程池数量 int nThreads=Runtime.getRuntime().availableProcessors(); //定义一个装载多线程返回值的集合 List<MonitorDetail> result= Collections.synchronizedList(new ArrayList<MonitorDetail>()); //创建线程池,这里定义了一个创建线程池的工具类,避免了创建多个线程池 ExecutorService executorService = ThreadPoolFactoryUtil.getExecutorService(nThreads); //遍历数据库取出的url if(list!=null&&list.size()>0) { for (MonitorProjectUrl monitorProjectUrl : list) { String url = monitorProjectUrl.getMonitorUrl(); //创建任务 ThreadTask threadTask = new ThreadTask(url, result); //执行任务 executorService.execute(threadTask); } //对数据进行操作 saveData(result); } }
任务:
public class ThreadTask implements Runnable{ //这里实现runnable接口 private String url; private List<MonitorDetail> list; public ThreadTask(String url,List<MonitorDetail> list){ this.url=url; this.list=list; } //把获取的数据进行处理 @Override public void run() { MonitorDetail detail = HttpClientUtil.send(url, MonitorDetail.class); list.add(detail); }}
文中案例为监控系统故没有关闭线程池
以上是本人的一些分享,希望和大家多多交流。如果哪里有不足,希望指出!
- 多线程之线程池的应用
- 多线程之线程池的应用
- 多线程之线程池的应用
- 多线程之线程池的应用
- 多线程之线程池Executor应用
- 【Java多线程与并发库】8.java5线程并发库之线程池的应用
- 【Java多线程与并发库】8.java5线程并发库之线程池的应用
- Java多线程与并发库高级应用之线程池
- Java多线程与并发库高级应用之线程池
- 多线程之线程池
- Java多线程之线程池的学习
- java多线程之--线程池的创建
- 多线程之线程池的简单实现
- 通过线程池使用多线程并发:ThreadPoolTaskExecutor 的应用例子
- 多线程并发库高级应用 之 java5中的线程并发库--线程池、Callable&Future
- 多线程并发库高级应用 之 java5中的线程并发库--线程池、Callable&Future
- java多线程并发库高级应用 之 java5中的线程并发库--线程池、Callable&Future
- 多线程之线程的中止
- springmvc 传参
- Android中Vitamio使用解析-电视台直播为例
- 浅谈CRC效验
- 迭代输出Map和List<Map<String,Object>>的方法
- 后台管理系统部署流程
- 多线程之线程池的应用
- java.util.ConcurrentModificationException异常处理
- 对比讨论URG-PSH
- 我的面试经历2
- url请求
- RocketMQ(前传)——消息队列
- java面试题
- httpclient4.5.3 使用
- xml报错 Parse Fatal Error :在实体引用中,实体名称必须紧跟在'&'后面