其实每个能够写代码的人都想完全掌握自己的代码的执行情况,但由于现在的高级语言封装的比较好,我们已经越来越难的控制一个程序的底层实现,更不用说完全掌控一个程序的执行时间了。所以我就一直在构思,能不能利用某些高级语言的一些特殊性质,达到控制某个方法执行时间的目的,即如果在时间范围内执行完成则正常结束,返回正确结果,否则强制结束并返回否定结果。经过一段时间的尝试、试验与修改,一个能够正常使用的解决方法已经出来了,下面我将通过每个类大致解释过程,源代码中的注释与解释也相当完善,可以到我的资源页下载全部源代码。附带的两个DEMO对使用方法的介绍页比较详细!
计时线程类:
package edu.ahnu.hpc.jundaixie;
import java.util.concurrent.CountDownLatch;
/**
* 计时线程类,通过线程的睡眠机制获取某一段时间。因为考虑到notify()、notifyAll()以及其他可能的方法会
* 唤醒睡眠中的线程导致InterruptedException,这会打断线程,影响线程的时间。所以一个较好的方法是将本
* 线程划分成若干段执行,从而尽可能额减小误差
* @author Jundai Xie
* @version 1.0
* @since 1.0
* Created: January 23rd,2013 Tekview Ltd,Lujiazui,Shanghai
* Modify:
*
*/
public class TimeCounter extends Thread {
@SuppressWarnings("deprecation")
@Override
/**
* 重写的Object的finalize()
* 强制关闭线程并释放可能关联的系统资源,因为stop()、destroy()方法都已经标记为不可用,而我需要一种
* 方法强制的关闭线程,这是因为用户需要传递一个线程进来,那个线程我也要关闭,而我却无法在源码里控制它,
* 所以要用强制的方式,虽然很不雅,但也没办法。。
*/
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
try{
Thread.yield();
}catch(Exception e){}
try{
this.interrupt();
}catch(Exception e){}
try{
this.stop();
}catch(Exception e){}
try{
this.destroy();
}catch(Exception e){}
super.finalize();
System.out.println("Thread TimeCounter Over");
}
/**
* 本线程需要执行的时间,以毫秒(ms)为单位
*/
private int mstime;
/**
* 线程执行时的睡眠小段,以毫秒为单位
*/
private int msless;
/**
* 统计当前线程数并使主线程等待的信号量
*/
public CountDownLatch m;
/**
* 构造函数
* @param time 线程执行的时间
* @param less 线程一小段执行的时间
*/
public TimeCounter(int time,int less){
mstime=time;
msless=less;
}
/**
* 线程运行的主要方法,就是在一小段内执行线程的睡眠
* 当线程设定的时间结束的时候抛出一个RuntimeException,并附带上 TimeOver 的结束信息
*/
public void run()
{
while(true){
if(mstime>=0 && mstime>=msless)
{
try {
Thread.sleep(msless);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mstime-=msless;
}
else if(mstime>=0 && mstime<msless)
{
try {
Thread.sleep(mstime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mstime=0;
}
System.out.println("Remaining "+mstime/1000.0+" seconds");// 打印剩余时间
if(mstime<=0){
m.countDown();
throw new RuntimeException("TimeOver");
}
}
}
}
该类是一个计时类,通过操作线程的睡眠时间达到计时的目的,并在时间结束的时候抛出一个运行时异常,告诉可以拦截到该异常的方法是时间结束了,可以结束本次执行了。该类的一个要点就是由于考虑到InterruptedException的存在,为了尽可能地减小误差,实际上不是让线程睡眠一个很长的时间,而是一段相对较小的时间段。
下面是UserThread类:
package edu.ahnu.hpc.jundaixie;
import java.util.concurrent.CountDownLatch;
/**
* 抽象类,继承自线程类,用户将自己的方法封装在threadPart()里面,然后创建该类的实例对象,传递到
* Intergrater类的实例里即可执行
* 这个类要求用户继承,并重写threadPart方法
* 具体的用法请参见Demo部分
* @author Jundai Xie
* @version 1.0
* @since 1.0
* Created: January 23rd,2013 Tekview Ltd,Lujiazui,Shanghai
* Modify:
*
*/
public abstract class UserThread extends Thread {
@SuppressWarnings("deprecation")
@Override
/**
* 重写的Object的finalize()
* 强制关闭线程并释放可能关联的系统资源,因为stop()、destroy()方法都已经标记为不可用,而我需要一种
* 方法强制的关闭线程,这是因为用户需要传递一个线程进来,那个线程我也要关闭,而我却无法在源码里控制它,
* 所以要用强制的方式,虽然很不雅,但也没办法。。
*/
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
try{
Thread.yield();
}catch(Exception e){}
try{
this.interrupt();
}catch(Exception e){}
try{
this.stop();
}catch(Exception e){}
try{
this.destroy();
}catch(Exception e){}
super.finalize();
super.finalize();
System.out.println("Thread UserThread Over");
}
/**
* 用户可以灵活应用的对象
*/
public Object userObj;
/**
* 控制线程同步等待的变量,从Intergrater传递过来
*/
public CountDownLatch m;
/**
* 如果需要返回某个对象,这个对象就是控制返回的
*/
public Object returned=null;
/**
* 本线程的执行部分,在执行完成之后抛出运行时错误,并附带"RunOver”提示信息
*/
public void run()
{
// 线程首先执行抽象方法,该方法已经在子类中实现,所以执行的是子类实现的方法
// 并且在最后强制要求返回对象,该对象可以为null,也可以是自定义方法中需要的某个返回参数
returned=threadPart();
// 线程的同步变量的监控数量减1,关于该参数的详细介绍参见 Intergrater中关于m的介绍
m.countDown();
// 抛出运行时错误
throw new RuntimeException("RunOver");
}
/**
* 这个函数需要用户重写,就是用户需要执行的部分,是线程的一部分
* 这里能返回你想要的任何对象,包括int、boolean
* @return 强制要求返回的对象,没有的话可以为null
*/
public abstract Object threadPart() ;
/**
* 使用者自由定义的函数,会在Intergrater线程快结束的时候调用,方便用户进行后期处理
* @return 根据用户需求决定
*/
public abstract Object userMethod();
}
UserThread类是一个抽象类,使用者在使用这个解决方案时自己的类必须继承自该类,并实现两个抽象方法,其中threadPart()是使用者需要执行的代码段放的位置,使用者也可以灵活地应用提供的对象进行特殊功能的构造,demo2就是一个很好的例子。
管理类:
package edu.ahnu.hpc.jundaixie;
import java.util.concurrent.CountDownLatch;
/**
* 这个类运行计时线程和用户线程,并接收RuntimeException判断是哪个先结束,如果用户线程先结束,还要得到用户
* 的返回对象
* 以下详细分析程序各个部分的执行以及原理:
* 本包内提供的类巧妙的应用了RuntimeException以及设定默认处理方法的方法,提供了一种控制程序或某个过程
* 执行时间的一种方法。大部分牵扯到中断等的操作都会提供设置超时时间,但有些时候却没有提供而又想很好的限制
* 某个方法的执行时间,就可以使用本包内提供的这种方式执行,具体方法请参见Demo
* 具体使用方法请参见Demo部分
*
* 目前没有解决的问题:
* 01.由于如果只操作线程无法很好的关闭线程以及释放其中的资源,所以这个版本只适合用户线程中不牵扯系统资源而且
* 很耗时的计算任务
* 02.程序运行时由于有许多异常,会打印相当多的不可控异常栈信息,对结果的查看很不便,建议将重要结果输出到文件,
* 以方便查看
*
* @author Jundai Xie
* @version 1.0
* @since 1.0
* Created: January 23rd,2013 Tekview Ltd,Lujiazui,Shanghai
* Modify:
*
*/
public class Intergrater extends Thread {
@SuppressWarnings("deprecation")
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
try{
this.interrupt();
}catch(Exception e){}
try{
this.stop();
}catch(Exception e){}
try{
this.destroy();
}catch(Exception e){}
super.finalize();
super.finalize();
System.out.println("Thread Intergrater Over");
}
/**
* 用户的执行线程,必须继承UserThread
*/
private UserThread thdExcuter;
/**
* 计时线程
*/
private TimeCounter tc;
/**
* 用户执行线程的返回对象
*/
private Object returner;
/**
* 让主线程等待这些子线程执行直到子线程结束执行的控制信号量
*/
public CountDownLatch m;
/**
* 构造函数,初始化两个线程信息,并设定默认的在线程中收到抛出异常的处理方法
* @param t1 线程需要执行的时间
* @param t2 线程每个小的执行片段
* @param ut 用户继承UserThread并创建的对象实例
*/
public Intergrater(int t1,int t2,UserThread ut)
{
/*
* 这里需要详细的解说下这个变量:
* CountDownLatch是一个信号量,控制着子线程与主线程之间的运行关系,它先初始化一个子线程数,在线程还未开始执行的时候
* 要赋予其含义:每个继承Runnable接口的类都要有一个CountDownLatch成员,并从这里初始化之后赋值给每个线程类的该成员。
* 在线程执行完成之后执行countDown方法,会使其初始记录的子线程个数减1,直到线程为0的时候才允许主线程执行。
* 好了分析以上,由于这里我设计的两个执行线程中只可能有一个会完全执行完成,肯定会有一个先后顺序逻辑上,所以这里虽然有三
* 个线程,而初始化的时候应该置2;而在得到运行时错误的时候再执行countDown的原因是让各个线程都完全处理好逻辑并完成返回
* 再结束线程,让主线程继续
*/
m=new CountDownLatch(2);
this.returner=null;
tc=new TimeCounter(t1,t2);
this.thdExcuter=ut;
this.tc.m=m;
this.thdExcuter.m=m;
/**
* 设置默认的线程中错误的处理方法
*/
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(){
/**
* 设置默认的线程中错误的处理方法,该方法需要判断哪个线程先结束,然后据此设置返回结果
* 之后还要关闭所有的线程,并减小信号量,让主线程启动,保证程序的正确执行
*/
public void uncaughtException(Thread t, Throwable e) {
// TODO Auto-generated method stub
String re=e.getLocalizedMessage();
try{
thdExcuter.userMethod();
}catch(Exception ed){}
System.out.println("Get Exception MSG="+re); // 第一遍打印
if("RunOver".equals(re))
{
returner=thdExcuter.returned;
}
else if("TimeOver".equals(re))
{
returner=null;
}
else return;
//System.out.println(re); // 第二遍打印
m.countDown();
try{
try {
thdExcuter.finalize();
} catch (Throwable e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}catch(Exception e1){
e1.printStackTrace();
}
try{
try {
tc.finalize();
} catch (Throwable e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}catch(Exception e2){
e2.printStackTrace();
}
//System.out.println(re); // 第三遍打印
e.printStackTrace();
}
});
}
/**
* 启动所有线程
*/
public synchronized void run()
{
this.tc.start();
this.thdExcuter.start();
}
public Object getReturner() {
return returner;
}
}
该类有两个线程成员变量,分别为计时线程和用户的执行线程(UserThread类的子类),并且本类设置了对线程中异常的监听与处理,通过对RuntimeException的处理,判断是一般的异常还是时间结束与运行完成,从而分别进行处理。我的语言表达不是很确切,还是看看两个具体的例子吧,注意细节!!
Demo1:
用户线程完成的是累加(纯耗时操作),而限制执行的时间是5秒。
import edu.ahnu.hpc.jundaixie.*;
/**
* 这里只是个简单的小例子,程序需要完成某个很大的循环,而我只想让该循环执行5秒。也可以修改线程让用户部分小于5秒
* 结束然后程序也同样会执行结束
* edu.ahnu.hpc.jundaixie包的使用方法:
* 1,新建一个类,继承该包下的UserTread类,并实现其虚方法threadPart()
* 2,threadPart()的实现规范:这个是需要在线程中执行的,放上你需要执行的代码,并在最后强制返回
* 3,之后要新建Intergrater类的实例,传递三个参数,分别为:
* 参数1:需要执行的时间,以毫秒计算
* 参数2:每个小的执行时间片段长度
* 参数3:第二步创建的对象
* 4,开始Intergrater线程
* 5,m是继承自UserThread类的公有成员变量,第二部创建的对象继承自UserThread,所以它也继承了这个公有的成员变量
* 而在这里开始await是为了让计时线程和执行线程都能够很好的执行,并完成返回参数的处理
* 6,完成,这个时候就可以查看想要的执行结果了,返回对象通过Intergrater.getReturner()方法获得,其可能为null
* 7,如果还有什么后续的处理,请放在userMethod()方法当中,线程内部会自动调用
* @author Jundai Xie
* Created: January 23rd,2013 Tekview Ltd,Lujiazui,Shanghai
*/
public class Demo1 extends UserThread {
@Override
/**
* 这个线程执行的是一直循环加1操作,返回null
*/
public Object threadPart() {
// TODO Auto-generated method stub
for(int i=0;i<5;i++)
{
for(int j=0;j<500000000;j++);
System.out.println("i="+i);
}
return null;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
long tStart = System.currentTimeMillis();
Demo1 d=new Demo1();
Intergrater itr=new Intergrater(5000,500,d);
itr.start();
try {
d.m.await();
} catch (InterruptedException ew) {
// TODO Auto-generated catch block
ew.printStackTrace();
}
System.out.println("\nReturn is = "+itr.getReturner());
long tEnd = System.currentTimeMillis();
System.out.println("It costs "+(tEnd-tStart)+" ms");
}
@Override
public Object userMethod() {
// TODO Auto-generated method stub
return null;
}
}
Demo2:
测试在192.168.0.2--192.168.0.254这个网段内哪些IP在被使用(达到了节省时间的目的)
import java.io.IOException;
import edu.ahnu.hpc.jundaixie.Intergrater;
import edu.ahnu.hpc.jundaixie.OutputToFile;
import edu.ahnu.hpc.jundaixie.UserThread;
/**
* 假如公司内网在192.168.0.2--192.168.0.254这样的一段网段内,我需要测试哪些IP正在被使用,而哪些则处于空闲状态,
* 简单点就需要 ping 一下就行了,但是如果IP没有被使用,则ping的时候就会消耗较多时间,这时就可以用该解决方案,快速
* 进行探测
*
* 探测这样的254个网络一共耗时60s左右!
*
* 探测ping一个IP耗时已得数据(公司内网):
* IP在被使用:78ms
* IP未被使用:4400+ms
*
* @author Jundai Xie
* January 24th,2013 09:09
*/
public class Demo2 extends UserThread {
Process p=null;
@Override
public Object threadPart() {
try {
p=Runtime.getRuntime().exec("ping -n 1 "+(String)this.userObj);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(null!=p){
try {
p.waitFor();// 由于这里不会被其他资源中断,所以
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return true;
}
public static void main(String[] args) {
long tStart = System.currentTimeMillis();
OutputToFile otf=new OutputToFile();
for(int i=2;i<255;i++){
String ip="192.168.0."+i;
System.out.println("\nTesting "+ip);
otf.append("Testing "+ip);
Demo2 demo=new Demo2();
demo.userObj=ip;
Intergrater itr=new Intergrater(200,100,demo); // 内网ping通通常小于100ms,200ms已经够长了
itr.start();
try {
demo.m.await();
} catch (InterruptedException ew) {
ew.printStackTrace();
}
boolean re=false;
if(null!=itr.getReturner()){
if((itr.getReturner()).equals(true)){
System.out.println("\nExist!");
re=true;
}
else System.out.println("\nFailed..");
}else System.out.println("\nFailed..");
if(re){
otf.appendLine(" Exist!");
}
else{
otf.appendLine(" Failed...");
}
}
long tEnd = System.currentTimeMillis();
System.out.println("It costs "+(tEnd-tStart)+" ms");
otf.appendLine("It costs "+(tEnd-tStart)+" ms");
otf.close();
}//
@Override
public Object userMethod() {
// TODO Auto-generated method stub
p.destroy();
return null;
}
}
考略到线程中抛出的大量不可控异常堆栈打印,所以建议将重要结果写入文件查看:
package edu.ahnu.hpc.jundaixie;
import java.io.FileWriter;
import java.io.IOException;
/**
* 考虑到输出结果的查看问题,写了这个类方便查看
* @author Jundai Xie
* January 24th,2013 09:48
*/
public class OutputToFile {
public String fileName=null;
public FileWriter fos=null;
public OutputToFile(){
this("d:\\CountTimeToRun.txt");
}
public OutputToFile(String path){
this.fileName=path;
try {
fos=new FileWriter(path);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public boolean append(String s){
boolean b=true;
try {
fos.write(s);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
b=false;
}
return b;
}
public boolean appendLine(String s){
boolean b=true;
try {
fos.append(s+"\r\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
b=false;
}
return b;
}
public void close(){
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
本文允许自由转载,附带源代码也允许自由下载、使用、修改,但请注明出处,如果您有什么建议或好等想法,也请联系我哦!