限制某方法执行时间的一个解决方案和实现

其实每个能够写代码的人都想完全掌握自己的代码的执行情况,但由于现在的高级语言封装的比较好,我们已经越来越难的控制一个程序的底层实现,更不用说完全掌控一个程序的执行时间了。所以我就一直在构思,能不能利用某些高级语言的一些特殊性质,达到控制某个方法执行时间的目的,即如果在时间范围内执行完成则正常结束,返回正确结果,否则强制结束并返回否定结果。经过一段时间的尝试、试验与修改,一个能够正常使用的解决方法已经出来了,下面我将通过每个类大致解释过程,源代码中的注释与解释也相当完善,可以到我的资源页下载全部源代码。附带的两个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();
		}
	}
}

 

本文允许自由转载,附带源代码也允许自由下载、使用、修改,但请注明出处,如果您有什么建议或好等想法,也请联系我哦!

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值