0 0

关于ExecutorService 线程池的问题20

1.背景:
想要实现一个多线程扫描的功能,使用spring @Scheduled 设定扫描计划,每隔30s创建线程执行扫描任务.

2.出现的问题:
Scheduled 计划执行几天的时间后就会不再执行(方法里有日志输出,但不再打印),出现此问题后,系统还是可以正常访问,log中没有扫描日志

网上查询 了一下,有人说是内存过低有可能导致Scheduled停止.

3.请问下面的代码有没有什么问题会导致内存泄露,关键代码已用红色标注

//相关引用

@Component
public class ScheduledScanTimer {
	private final Logger log = Logger.getLogger(getClass());
	private SimpleDateFormat sf = new SimpleDateFormat("HH:mm");
	private FiberScanTaskService scanTaskService;
	private FiberService fiberService;
	private FiberLogInfoService fiberLogInfoService;
	private FiberSDFIService fiberSDFIService;

	
	@Scheduled(fixedDelay = 30000)
	public void fiberScan() {
		log.info("@Scheduled  start......................");
		List<Future<List<ScanResult>>> scanList = new ArrayList<Future<List<ScanResult>>>();// 扫描结果
		// 整理扫描结果
		StringBuffer scanResultStr = new StringBuffer();
		StringBuffer fiberScanIDs = new StringBuffer();
		try {
			// 获取检查任务sql语句
			String sql = getScanSql();
			List<FiberScanTask> taskList = scanTaskService.getFiberScanTaskByScanTime(sql);
			// 检查是否有执行任务
			if (taskList != null && taskList.size() > 0) {
				log.info("have task  to execute!");
				for (FiberScanTask task : taskList) {
					// 设置任务状态为扫描中...
					task.setTaskStatus(1);
					scanTaskService.editFiberScanTask(task, false);
					// 获取光纤Ids
					String fiberIds = task.getFiberIds();
					// 对要扫描的光纤Ids按照sfdi分组
					List<ScanThread> scanThreadList = fiberService.getThreadGroupByFiberIds(fiberIds);
					// 初始化指定个数的线程池
					ExecutorService pool  = Executors.newFixedThreadPool(taskList.size());
					for (ScanThread st : scanThreadList) {
					    Callable<List<ScanResult>> callable = new FiberScanner(st.getFiberIds(), fiberService, fiberLogInfoService, fiberSDFIService);
					    Future<List<ScanResult>> future = pool.submit(callable);
					    scanList.add(future);
					}
					// 遍历线程返回结果
					for (Future<List<ScanResult>> fu : scanList) {
						List<ScanResult> resList = fu.get();
						for (ScanResult scanResult : resList) {
							scanResultStr.append(scanResult.getScanResultStr() + "<br>");
							fiberScanIDs.append(scanResult.getScanRecordId() + ",");
						}
					}
					// 关闭线程池
					pool.shutdown();
				}
			} else {
				log.info("no execute task !");
				// 更新无效任务状态
				updateInvalidTask();
			}
		} catch (InterruptedException e) {
			log.error("FiberController.java fiberScan()," + e.getMessage());
			e.printStackTrace();
		} catch (ExecutionException e) {
			log.error("FiberController.java fiberScan()," + e.getMessage());
			e.printStackTrace();
		} finally {
			scanList.clear();
			scanList = null;
			scanResultStr = fiberScanIDs = null;
		}
	}

}

   

   如果换成 newCachedThreadPool 修改成下面的会不会好些?

  

//相关引用

@Component
public class ScheduledScanTimer {
	private final Logger log = Logger.getLogger(getClass());
	private SimpleDateFormat sf = new SimpleDateFormat("HH:mm");
	private FiberScanTaskService scanTaskService;
	private FiberService fiberService;
	private FiberLogInfoService fiberLogInfoService;
	private FiberSDFIService fiberSDFIService;
	private ExecutorService pool ;
	
	@Scheduled(fixedDelay = 30000)
	public void fiberScan() {
		log.info("@Scheduled  start......................");
		List<Future<List<ScanResult>>> scanList = new ArrayList<Future<List<ScanResult>>>();// 扫描结果
		// 整理扫描结果
		StringBuffer scanResultStr = new StringBuffer();
		StringBuffer fiberScanIDs = new StringBuffer();
		try {
			// 获取检查任务sql语句
			String sql = getScanSql();
			List<FiberScanTask> taskList = scanTaskService.getFiberScanTaskByScanTime(sql);
			// 检查是否有执行任务
			if (taskList != null && taskList.size() > 0) {
				log.info("have task  to execute!");
				// 初始化指定个数的线程池
				pool = Executors.newCachedThreadPool();
				for (FiberScanTask task : taskList) {
					// 设置任务状态为扫描中...
					task.setTaskStatus(1);
					scanTaskService.editFiberScanTask(task, false);
					// 获取光纤Ids
					String fiberIds = task.getFiberIds();
					// 对要扫描的光纤Ids按照sfdi分组
					List<ScanThread> scanThreadList = fiberService.getThreadGroupByFiberIds(fiberIds);
					for (ScanThread st : scanThreadList) {
					     Callable<List<ScanResult>> callable =new FiberScanner(st.getFiberIds(),fiberService,fiberLogInfoService,fiberSDFIService);
					     Future<List<ScanResult>> future = pool.submit(callable);
					     scanList.add(future);
					}
					// 遍历线程返回结果
					for (Future<List<ScanResult>> fu : scanList) {
						List<ScanResult> resList = fu.get();
						for (ScanResult scanResult : resList) {
							scanResultStr.append(scanResult.getScanResultStr() + "<br>");
							fiberScanIDs.append(scanResult.getScanRecordId() + ",");
						}
					}
				}
			} else {
				log.info("no execute task !");
				// 更新无效任务状态
				updateInvalidTask();
			}
		} catch (InterruptedException e) {
			// 关闭线程池
			pool.shutdown();
			log.error("FiberController.java fiberScan()," + e.getMessage());
			e.printStackTrace();
		} catch (ExecutionException e) {
			// 关闭线程池
			pool.shutdown();
			log.error("FiberController.java fiberScan()," + e.getMessage());
			e.printStackTrace();
		} finally {
			scanList.clear();
			scanList = null;
			scanResultStr = fiberScanIDs = null;
		}
	}

}

 

2014年8月08日 09:35

2个答案 按时间排序 按投票排序

0 0

从看代码上看,线程池的数量与每批任务数量相等,处理任务结果的方式是阻塞式的,
如果某一任务没有处理结束,那么本次定时器会一直阻塞,下次定时器执行时,若又出现类似情况,则又会出现定时器阻塞,
这样每次定时器执行时,线程池都得不到释放,则线程池会增加,线程也会越来越多,时间久了,就会出现楼主说的现象

个人觉得
  在处理任务结果,可以使用带超时机制的get方法,
   同时在定时器执行时,需要判断上一次是否执行完成了,否则不能开始新的扫描过程,不然就是找“死”的结果。

2014年8月12日 20:25
0 0

任务没割30秒执行一次,每次都能在30秒之内完成么,如果没有的话,那每次任务占用的内存空间都不能释放,可能会导致内存占用比较大,可以监控下jvm堆大小、回收情况。

2014年8月08日 12:55

相关推荐

    运用JAVA的concurrent.ExecutorService线程池实现socket的TCP和UDP连接JAVA语言

    运用JAVA的concurrent.ExecutorService线程池实现socket的TCP和UDP连接

    ExecutorService线程池

    ExecutorService线程池是Java并发编程中的核心组件,它位于`java.util.concurrent`包下,是`java.util.concurrent.Executor`接口的一个实现。ExecutorService提供了一种管理线程的方式,允许我们创建、管理和控制...

    在spring boot中使用java线程池ExecutorService的讲解

    在 Spring Boot 中使用 Java 线程池 ExecutorService 的讲解 Spring Boot 作为一个流行的 Java 框架,提供了许多便捷的功能来帮助开发者快速构建应用程序。其中之一就是使用 Java 线程池 ExecutorService 来管理...

    运用JAVA的concurrent.ExecutorService线程池实现socket的TCP和UDP连接.doc

    根据提供的文档标题、描述以及部分内容,我们可以总结出以下关于如何运用Java中的`concurrent.ExecutorService`线程池实现socket的TCP和UDP连接的关键知识点: ### 1. 理解Java中的`concurrent.ExecutorService` `...

    关于线程池的代码demo

    线程池是多线程编程中的一个重要概念,它在Java中通过`java.util.concurrent`包中的`ExecutorService`接口及其实现类实现。线程池的使用能够有效地管理和控制线程资源,避免频繁创建和销毁线程带来的性能开销,提高...

    Java ExecutorService四种线程池使用详解

    在Java多线程编程中,`ExecutorService` 是线程池的核心接口,它提供了一种管理线程的方式,包括创建、调度和终止线程。Java的`java.util.concurrent`包中提供了`ExecutorService`的实现类,特别是通过`Executors`...

    Java 线程池ExecutorService详解及实例代码

    线程池可以预先创建一定数量的线程,待有任务提交时直接复用,避免了频繁创建和销毁线程的问题。此外,线程池还能通过调整线程数量来控制并发度,防止过多线程导致的资源消耗和上下文切换过度。 2. ExecutorService...

    Java线程池使用说明

    在JDK 1.5版本之前,Java对线程池的支持非常有限,而在JDK 1.5之后,加入了java.util.concurrent包,其中包含了一系列关于线程池的接口和类,极大地丰富了线程池的应用场景和管理方式。 线程池的主要作用是限制系统...

    线程池管理线程demo

    在Java中,线程池通过`java.util.concurrent`包中的`ExecutorService`接口和其相关类实现。下面将详细阐述线程池的工作原理、优势以及如何在实际应用中创建和管理线程池。 1. **线程池工作原理** 线程池由一组可...

    springmvc+spring线程池处理http并发请求数据同步控制问题

    1. Spring提供了一个名为ThreadPoolTaskExecutor的实现,它基于Java的ExecutorService接口,允许我们自定义线程池配置,如核心线程数、最大线程数、队列容量、超时时间等。 2. 通过在配置文件中声明一个...

    Java中多线程的使用线程池.docx

    - **关闭线程池**:在所有任务执行完毕后,调用 `executorService.shutdown()` 或 `executorService.shutdownNow()` 来优雅地关闭线程池。 6. **示例代码**: - `MyThread` 类实现了`Runnable`接口,`run`方法...

    java 线程池常用方法

    线程池通过`Executor`接口和`ExecutorService`接口提供了一套强大的机制,允许开发者高效地创建、管理和控制线程的执行。 1. **创建任务** 创建任务通常需要实现`Runnable`接口或`Callable`接口。`Runnable`接口...

    TOMCAT的线程池源码

    Tomcat的线程池实现是基于Apache Commons JMX的ExecutorService,它是对Java标准库ExecutorService的一种扩展,增加了更多的监控和管理功能。 Tomcat的线程池主要由`org.apache.tomcat.util.threads.TaskQueue`和`...

    java线程池工具--ExecutorService,简单例子

    NULL 博文链接:https://x125858805.iteye.com/blog/2191873

    JAVA线程池原理以及几种线程池类型介绍

    在Java中,`java.util.concurrent.ExecutorService`接口定义了线程池的基本行为。常用的实现类包括: 1. **FixedThreadPool**:创建一个固定大小的线程池,线程数量由用户指定。当所有线程都在执行任务时,新提交的...

    关闭线程池

    这些异常反映了网络通信和线程池操作中的常见问题,如连接超时、线程池饱和和套接字关闭。捕获这些异常并记录日志有助于调试和监控系统的健康状态。 总的来说,关闭线程池是一个涉及系统稳定性、资源管理和异常处理...

    java线程池封装j

    Java线程池由`java.util.concurrent`包中的`ExecutorService`接口和其子类实现。其中,最常用的是`ThreadPoolExecutor`类,它提供了丰富的参数用于定制线程池的行为。线程池的核心组件包括: - **核心线程数...

    Java/Android线程池演示Demo

    Java的`java.util.concurrent`包提供了`ExecutorService`接口,它是线程池的主要入口。通过实现这个接口,我们可以控制线程的执行方式,如设置最大线程数、处理任务队列等。`ExecutorService`通常与`...

    Executor,Executors,ExecutorService比较.docx

    通常,我们不会直接使用`Executor`,而是使用它的子接口`ExecutorService`,因为`ExecutorService`提供了更多的功能,如任务的提交、管理和关闭线程池。 2. **Executors** `Executors`是`java.util.concurrent`包...

Global site tag (gtag.js) - Google Analytics