package org.hy.common.thread; import org.hy.common.Help; /** * 任务对象 * * <O>指线程自有的缓存的对象类型 * * @author ZhengWei(HY) * @version V1.0 2011-06-08 * V2.0 2017-02-21 添加:isStop状态控制属性。任务是否停止执行。任务还未绑定线程执行前被停止。 * 对于已绑定线程正在执行中的是不生效的。 */ public abstract class Task<O> implements Runnable { /** 执行任务的线程。为了安全与功能相关独立,不对实现类与子类开放此属性 */ private ThreadBase thread; /** 任务组。可选属性,即任务可以在没有组的情况独立运行。为了安全与功能相关独立,不对实现类与子类开放此属性 */ private TaskGroup taskGroup; /** 任务编号 */ protected long taskNo; /** 任务的名称 */ protected String taskName; /** 任务的类型 */ protected String taskType; /** 任务是否完成。为了安全与功能相关独立,不对实现类与子类开放此属性 */ private boolean isFinish; /** * 任务是否停止执行。任务还未绑定线程执行前被停止。 * 对于已绑定线程正在执行中的是不生效的。 * 默认为:false。 */ private boolean isStop; /** 窗口输出的缓存 */ protected StringBuilder printBuffer; /** SQL语句的缓存 */ protected StringBuilder sqlBuffer; /** * 获取任务描述 * * @return */ public abstract String getTaskDesc(); /** * 执行任务的方法 * * 常规情况下:一定要在 execute()执行后,调用 this.finishTask(); 方法。 * 即使 execute() 异常,也应当调用 this.finishTask(); 方法。 * 所以,建议如下方法写代码 * try * { * ... ... * } * finally * { * this.finishTask(); * } */ public abstract void execute(); /** * 获取任务编号。 * * 因为每个任务对象都应当有独立的编号顺序。 * 即每个Task实现类,实例化的第一个类的编号应当都从0开始编号,所以这个工作就由实现者来完成。 * * @return */ public abstract long getSerialNo(); /** * 构造器 * * @param i_TaskType 任务类型 */ public Task(String i_TaskType) { if ( Help.isNull(i_TaskType) ) { throw new NullPointerException("Task Type is null"); } this.taskType = i_TaskType; this.taskNo = this.getSerialNo(); this.taskName = this.taskType + "-" + this.taskNo; this.isFinish = false; this.isStop = false; this.printBuffer = new StringBuilder(); this.sqlBuffer = new StringBuilder(); } /** * 不写被继承或重写的方法。 * 对于实现类,请使用 this.execute() 方法 */ public final void run() { this.execute(); } /** * 设置与任务对应的执行线程。 * * 为了安全与功能相关独立,不对外提供 getThread() 方法。 * * @param thread */ public void setThread(ThreadBase thread) { this.thread = thread; } /** * 设置任务组。 * * 当任务添加到任务组中时,任务组默认会调用 setTaskGroup() 方法,将任务组自己设置为任务的任务组属性。 * 即,一般不用主动调用此方法。 * * @param i_TaskGroup */ public void setTaskGroup(TaskGroup i_TaskGroup) { this.taskGroup = i_TaskGroup; } /** * 获取:任务组。可选属性,即任务可以在没有组的情况独立运行。为了安全与功能相关独立,不对实现类与子类开放此属性 */ public TaskGroup getTaskGroup() { return this.taskGroup; } /** * 获取任务的名称 * * @return */ public String getTaskName() { return taskName; } /** * 设置任务的名称。 * * 即使不由外围程序设置任务名称,它也有默认的名称。 * * @param taskName */ public void setTaskName(String taskName) { this.taskName = taskName; } /** * 获取任务的编号 * * @return */ public long getTaskNo() { return taskNo; } /** * 获取任务类型 * * @return */ public String getTaskType() { return this.taskType; } /** * 获取任务的运行信息。 * 主要用于窗口的显示。 * 对于同一类型任务,会等同类任务都执行完成后,再统一顺序的显示其运行信息。 * * @return */ public String getTaskRunInfo() { return this.printBuffer.toString(); } /** * 获取任务的运行SQL语句。 * 主要用于数据库记录。 * 对于同一类型任务,会等同类任务都执行完成后,再统一顺序的执行其SQL。 * * @return */ public String getTaskRunSQL() { return this.sqlBuffer.toString(); } /** * 清空 printBuffer 和 sqlBuffer 中的缓存内容 */ public void clearBuffer() { this.printBuffer = new StringBuilder(); this.sqlBuffer = new StringBuilder(); } /** * 设置线程自有的缓存 * * 建议在线程执行中使用,在线程执行完成后,不要在使用了。 * 因为,某些瞬间,任务对应的线程已分配给其它任务。 * * @param i_Obj */ public void setThreadCache(O i_Obj) { if ( this.thread != null ) { this.thread.setCache(i_Obj); } } /** * 刷新一下监视窗口中的信息 */ public void refreshWatchInfo() { if ( this.thread != null ) { this.thread.refreshWatchInfo(); } } /** * 获取线程附加缓存。是预留给继承者使用的。继承者可以有选择的使用。 * * 建议在线程执行中使用,在线程执行完成后,不要在使用了。 * 因为,某些瞬间,任务对应的线程已分配给其它任务。 */ @SuppressWarnings("unchecked") public O getThreadCache() { Object v_Ret = null; if ( this.thread != null ) { v_Ret = this.thread.getCache(); } else { return null; } if ( v_Ret != null ) { return (O)v_Ret; } else { return null; } } /** * 线程序号 * * @return */ public String getThreadNo() { if ( this.thread != null ) { return this.thread.getThreadNo(); } else { return ""; } } /** * 获取线程运行状态。 * * 不建议使用。因为,某些瞬间,任务对应的线程已分配给其它任务。 * * 某些瞬间指:任务已完成,对应线程已空闲。但之后线程会再分配新的任务,此时的线程状态就不在准确了。 * * @return */ @Deprecated public ThreadRunStatus getThreadRunStatus() { if ( this.thread != null ) { return this.thread.getThreadRunStatus(); } else { return ThreadRunStatus.$Exception; } } /** * 判断任务是否完成 * * @return */ public boolean isFinishTask() { return this.isFinish; } /** * 准备 -- 任务在开始执行前的准备动作。 * * 它应由 TaskPool 或 ThreadPool 调用。 * * 对于用户来说,不用主动的调用。 */ public void ready() { this.isFinish = false; this.isStop = false; } /** * 标记任务执行完成。 * * 此方法主要用于 this.execute() 方法中使用。是由实现任务的类型来调用的。 * 如果实现任务的类不在 this.execute() 中调用 this.finishTask() ,则任务一直运行。 * * 此外,不提供如 startTask() 方法及功能。 * 而有引入 TaskPool 池的概念,由它来实现线程Thread与任务Task的绑定和启动。 */ public void finishTask() { if ( this.taskGroup != null ) { this.taskGroup.taskFinish(this); } if ( this.thread != null ) { this.thread.finishTask(); } this.isFinish = true; } /** * 任务是否停止执行。任务还未绑定线程执行前被停止。 * 对于已绑定线程正在执行中的是不生效的。 * 默认为:false。 */ public boolean isStop() { return isStop; } /** * 任务是否停止执行。任务还未绑定线程执行前被停止。 * 对于已绑定线程正在执行中的是不生效的。 * 默认为:false。 * * @param isStop */ public void stopTasksNoExecute() { this.isStop = true; } /** * 获取任务组中任务数量 * * @return */ public int getTaskGroupSize() { if ( this.taskGroup != null ) { return this.taskGroup.size(); } else { return 1; } } protected void finalize() throws Throwable { this.printBuffer = null; this.sqlBuffer = null; // 在判断是否“完成”后,再对线程进行处理,就是怕任务已完成了,但线程已分配给其它任务来使用,造成错误“停止”线程的问题 if ( !this.isFinish ) { finishTask(); } super.finalize(); } }