我们系统中经常有耗费时间长的任务,但客户端往往需要马上得到回应。这时我们就可以如下步骤实现:

1、客户端发起请求执行任务(选定条件,下载报表);

2、首先将任务ID及开始时间,起始状态记录到数据库表中;

3、另起一个后台线程去执行这个耗时任务(比如生成报表);

4、线程执行成功或失败状态记录到数据库;

5、客户通过异步查询数据(下载报表或其他操作)。

好了,大致步骤我们清楚了。假如这个耗时任务一直执行,而且和消耗系统资源。我们往往想放弃这个任务的执行,再缩小范围执行更小的任务执行。那我们如何实现呐!

话不多说,直接上代码:

1.首先我们实现一个线程管理工具:

import java.sql.DriverManager
import java.util.concurrent.ConcurrentHashMap import org.slf4j.LoggerFactory import scala.util.{Failure, Success, Try} /**
* 类功能描述:报表线程管理器
*
* @author WangXueXing create at 18-11-2 上午11:35
* @version 1.0.0
*/
object ReportThreadManager {
private val logger = LoggerFactory.getLogger(getClass)
/**
* 报表ID与对应线程map
*/
val REPORT_THREAD_MAP: ConcurrentHashMap[Long, Thread] = new ConcurrentHashMap() /**
* 将对应报表子线程放入线程池
*
* @param reportId 报表ID
* @param thread 对应子线程
*/
def put(reportId: Long, thread: Thread): Unit = {
REPORT_THREAD_MAP.put(reportId, thread)
} /**
* 获取对应报表线程
* @param reportId 报表ID
* @return
*/
def get(reportId: Long): Thread ={
REPORT_THREAD_MAP.get(reportId)
} /**
* 将对应报表子线程移除线程池
* @param reportId 报表ID
*/
def remove(reportId: Long): Unit ={
REPORT_THREAD_MAP.remove(reportId)
} /**
* 销毁指定报表子线程
* @param reportId 报表ID
*/
def deploy(reportId: Long)={
val thread = REPORT_THREAD_MAP.get(reportId)
if(thread != null){
Try{
if(!thread.isInterrupted){
logger.info(s"线程:${reportId} 开始被结束") logger.info("before interrupt")
thread.getStackTrace.foreach(println) thread.interrupt() Thread.sleep(10)
logger.info("after interrupt")
thread.getStackTrace.foreach(println)
}
} match {
case Success(x) => logger.info(s"线程:${reportId} 被成功杀死")
case Failure(e) => logger.error(s"线程:${reportId} interrupt 失败", e)
}
REPORT_THREAD_MAP.remove(reportId)
}
} val thread1 = new Thread(new Runnable {
override def run(): Unit = {
ReportThreadManager.deploy(1)
println(s"thread 1 killed")
}
}) def main(args: Array[String]): Unit = {
Class.forName("org.apache.hive.jdbc.HiveDriver")
val con = DriverManager.getConnection("jdbc:hive2://192.168.71.127:10000/finance", "goods", null)
val stmt = con.createStatement
var res = stmt.executeQuery("SELECT company_name,store_code,store_name,source_date,trade_type,trade_no,third_party_payment_no,business_type,cast(business_amount as decimal(20, 2)) business_amount,cast(service_charge as decimal(20, 4)) service_charge,trade_time,customer_account,updated_at,created_at FROM t_pay_source_data")
while(res.next()){
println(res.getString(1))
}
}
}
此工具可以实现根据任务ID添加当前任务执行线程,也可以从线程池移除此线程,根据任务ID中断线程。 2.如下任务执行过程及如何调用线程管理及中断线程:
import java.io.{File, FileInputStream}

import com.today.api.financereport.scala.request.ReportInfo
import com.today.api.financereport.scala.response.ServiceResponse
import com.today.service.financereport.action.{ExportReportRecordFailureAction, ExportReportRecordSuccessAction, StartExportReportAction}
import com.today.service.financereport.common.ReportThreadManager
import com.today.service.financereport.dto.{ExportReportFailureInput, ExportReportSuccessInput, ReportOutput}
import com.today.service.financereport.util.{Debug, OssUtil}
import org.slf4j.LoggerFactory import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.control.NonFatal
import scala.util.{Failure, Success} /**
* 报表导出流程限定类
*
* @author BarryWang create at 2018/5/17 9:15
* @version 0.0.1
*/
trait ReportAction {
protected val logger = LoggerFactory.getLogger(getClass)
/**
* 报表导出流程
* @return
*/
def execute: ServiceResponse = {
var result:ServiceResponse = null
var count = Counter.count
logger.info(s"---1生成报表前运行个数:${count}----------")
if(count >= Counter.MAX_COUNT.intValue()){
result = ServiceResponse("405", s"目前有多个报表正在导出,请5分钟后再操作,谢谢!")
} else {
Counter.increment
count = Counter.count
logger.info(s"---2启动生成报表运行个数:${count}----------")
var reportId: Long = -1
try {
//1. 前置检查
preCheck //2. 开始生成报表纪录
val reportInfo = setReportInfo
reportId = startGenerate //3. 生成报表处理
val output: Future[ReportOutput] = Future {
Debug.reset()
//添加当前子线程到线程管理池
ReportThreadManager.put(reportId, Thread.currentThread())
//1) 加载模板
val templateInfo = Debug.trace(s"${reportInfo.reportType}-loadTemplate:")(loadTemplate(setTemplatePath))
//2) 生成报表
Debug.trace(s"${reportInfo.reportType}-generateReport:")(generateReport(reportId, templateInfo))
//3) 保存报表
val output = Debug.trace(s"${reportInfo.reportType}-saveReport:")(saveReport(templateInfo.localFile)) //将此子线程从线程管理池移除
ReportThreadManager.remove(reportId) Debug.info()
output
}
output.onComplete {
case Success(v) => {
successGenerate(ExportReportSuccessInput(reportId, v))
Counter.decrement
count = Counter.count
logger.info(s"---3结束报表生成运行个数:${count}----------")
}
case Failure(e) => {
failureGenerate(ExportReportFailureInput(reportId, e))
Counter.decrement
count = Counter.count
logger.info(s"---3结束报表生成运行个数:${count}----------")
}
} //4. 后置检查
postCheck result = ServiceResponse("200", "请到导出管理查看或下载报表")
} catch {
case NonFatal(e) =>
Counter.decrement
count = Counter.count
logger.info(s"---3结束报表生成运行个数:${count}----------")
failureGenerate(ExportReportFailureInput(reportId, e))
throw e
} finally {}
}
result
} /**
* 前置条件检查:动作、状态等业务逻辑
*/
def preCheck /**
* 设置报表信息
* @return
*/
def setReportInfo: ReportInfo /**
* 设置模板路径
* @return
*/
def setTemplatePath: String /**
* 开始生成报表纪录
*/
def startGenerate(): Long = {
new StartExportReportAction(setReportInfo).execute
} /**
* 加载模板
* @param templatPath
*/
def loadTemplate(templatPath: String): ExcelTemaplateInfo = {
val suffix = isZip match {
case true => ".zip"
case false => ".xlsx"
}
//生成本地文件
val localFile = File.createTempFile(downloadFileName+"_", suffix)
ExcelTemaplateInfo(templatPath, localFile)
} /**
* 下载文件名
* @return
*/
def downloadFileName: String = setReportInfo.reportType.name /**
* 根据数据生成报表
* @return
*/
def generateReport(reportId: Long, templateInfo: ExcelTemaplateInfo) /**
* 将生成在本地的报表上传到阿里SSO
* @param localFile
* @return
*/
def saveReport(localFile: File): ReportOutput = {
val fileUrl = OssUtil.uploadFileStream(new FileInputStream(localFile), localFile.getName)
localFile.deleteOnExit()
val suffix = isZip match {
case true => ".zip"
case false => ".xlsx"
}
// OssUtil.downloadFile(fileUrl, "/home/barry/data/1122322"+suffix)
ReportOutput(fileUrl)
} /**
* 最终生成报表是否为Zip
* @return
*/
def isZip: Boolean /**
* 成功生成报表纪录
* @param result: ExportReportSuccessInput
*/
def successGenerate(result: ExportReportSuccessInput): Unit = {
new ExportReportRecordSuccessAction(result).execute
} /**
* 失败生成报表纪录
* @param result: ExportReportFailureInput
*/
def failureGenerate(result: ExportReportFailureInput): Unit = {
new ExportReportRecordFailureAction(result).execute
} /**
* 后置检查
*/
def postCheck = {}
} 3.客户端触发中断当前任务线程:
import com.today.api.financereport.scala.response.ServiceResponse
import com.today.service.commons.Action
import com.today.service.financereport.action.sql.ExportReportRecordActionSql
import com.today.service.financereport.common.ReportThreadManager /**
* 类功能描述:报表删除Action
*
* @author WangXueXing create at 18-11-2 下午2:17
* @version 1.0.0
*/
class DeleteExportReportAction(id: Long) extends Action[ServiceResponse]{
override def preCheck: Unit = {} override def action: ServiceResponse = {
val result = ExportReportRecordActionSql.deleteExportReportById(id)
result match {
case 0 => ServiceResponse("501","删除报表失败")
case _ => {
//删除报表的同时结束生成报表线程
new Thread(new Runnable {
/**
* 延迟退出系统
*/
override def run(): Unit = {
Thread.sleep(50)
ReportThreadManager.deploy(id)
}
}).start()
ServiceResponse("200","success")
}
}
}
}

Java线程监控及中断的更多相关文章

  1. java线程池和中断总结

    目录 java线程池和中断总结 一. 线程池的使用 二. java中断机制 中断的处理 三. 线程间通信机制总结 java线程池和中断总结 本系列文是对自己学习多线程和平时使用过程中的知识梳理,不适合 ...

  2. 干货|宏巍软件之Java线程监控之旅

    宏巍软件 许向 大家好,我是上海宏巍信息技术有限公司(简称:宏巍软件)的许向,宏巍软件成立于2005年,是一家以电商ERP软件开发为主的高新技术科技型软件公司,致力于为大型网商和电子商务企业提供专业. ...

  3. Java线程中断机制-如何中断线程

    介绍: 对于线程一共分为五个状态:新建状态,就绪状态,阻塞状态,运行状态,死亡状态,有时候把阻塞状态又分为同步阻塞和等待阻塞. 有时想让主线程启动的一个子线程结束运行,我们就需要让这个子线程中断,不再 ...

  4. 性能测试三十二:监控之Java线程监控

    线程的五种状态 * 新建:new * 运行:runnable * 等待:waitting(无限期等待),timed waitting(限期等待) * 阻塞:blocked * 结束:terminate ...

  5. java 线程监控

    线程的五种状态 * 新建:new * 运行:runnable * 等待:waitting(无限期等待),timed waitting(限期等待) * 阻塞:blocked * 结束:terminate ...

  6. 基于 JVMTI 实现 Java 线程的监控(转)

    随着多核 CPU 的日益普及,越来越多的 Java 应用程序使用多线程并行计算来充分发挥整个系统的性能.多线程的使用也给应用程序开发人员带来了巨大的挑战,不正确地使用多线程可能造成线程死锁或资源竞争, ...

  7. lesson6:java线程中断

    正常的情况下,业务系统都不会去中断它的线程,但是由于一些特殊情况的发生,线程已经不能正常结束了,并且此类线程已经影响到业务系统提供服务的能力,如果系统设计的健壮,便会通过监控线程去主动的中断此类线程. ...

  8. Linux系统监控命令及如何定位到Java线程

    >>PID.TID的区分 uid是user id,即用户id,root用户的uid是0,0为最高权限,gid是group id,用户组id,使用 id 命令可以很简单的通过用户名查看UID ...

  9. Java并发编程(二)线程任务的中断(interrupt)

    使用interrupt()中断线程 当一个线程运行时,另一个线程可以调用对应的Thread对象的interrupt()方法来中断它,该方法只是在目标线程中设置一个标志,表示它已经被中断,并立即返回. ...

随机推荐

  1. postman的安装与使用(模拟请求)

    最近需要测试产品中的REST API,无意中发现了PostMan这个chrome插件,把玩了一下,发现postman秉承了一贯以来google工具强大,易用的特质.独乐乐不如众乐乐,特此共享出来给大伙 ...

  2. 推荐《用Python进行自然语言处理》中文翻译-NLTK配套书

    NLTK配套书<用Python进行自然语言处理>(Natural Language Processing with Python)已经出版好几年了,但是国内一直没有翻译的中文版,虽然读英文 ...

  3. tkinter做一个简单的登陆页面

    做一个简单的登陆页面 import tkinter wuya = tkinter.Tk() wuya.title("wuya") wuya.geometry("900x3 ...

  4. yii批量插入的方法

    public function insertSeveral($table, $array_columns) { $sql = ''; $params = array(); $i = 0; foreac ...

  5. 【转载】JavaScript基础知识体系

    前言 最近总是有一种感觉,对于知识没有积淀,很多时候都是忘记了哪里就去查一下,比如JS这种语言,很是浪费时间,如果能够把这些知识形成知识体系塞进大脑,做到即用即取就好了,那么就可以借助思维导图来帮助我 ...

  6. python之@property

    在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改: s = Student() s.score = 9999 这显然不合逻辑.为了限制score的 ...

  7. Redis系列-远程连接redis

    假设两台redis服务器,ip分别为:192.168.1.101和192.168.1.103,如何在101上通过redis-cli访问103上的redis呢?在远程连接103之前,先讲下redis-c ...

  8. 关于Kafka __consumer_offests的讨论

    众所周知,__consumer__offsets是一个内部topic,对用户而言是透明的,除了它的数据文件以及偶尔在日志中出现这两点之外,用户一般是感觉不到这个topic的.不过我们的确知道它保存的是 ...

  9. BZOJ_5015_[Snoi2017]礼物_矩阵乘法

    BZOJ_5015_[Snoi2017]礼物_矩阵乘法 Description 热情好客的请森林中的朋友们吃饭,他的朋友被编号为 1-N,每个到来的朋友都会带给他一些礼物:.其中,第 一个朋友会带给他 ...

  10. Mysql 上亿级数据导入Hive思路分享

    前提条件: 数据库容量上亿级别,索引只有id,没有创建时间索引 达到目标: 把阿里云RDS Mysql表数据同步到hive中,按照mysql表数据的创建时间日期格式分区,每天一个分区方便查询 每天运行 ...