java中限制方法运行时常的一个小工具类

  • 说明
  • 设计思路
  • 开始编码


说明

由于是本人初次设计,肯定有很多bug,不建议大家使用,可以用来参考学习

设计思路

监听java方法:

  1. 多线程实现监听 :创建一个守护线程,同时维护一个队列。将需要监听的方法放入队列中;
  2. 接口函数创建监听对象 :定义一个接口,将被监听的方法通过传参的方式传递到工具类中;

开始编码

  1. 准备工作,创建工具类,定义一个线程对象和队列,并设置一些后面需要用到的变量 类变量.
public class RunTimeoutUtil implements Runnable {
	//该类的单例(本类中使用,与调用者无关)
    private static RunTimeoutUtil instance;
    //本类中的监听线程
    private Thread thread;
    //被监听任务的队列,(Node是对一个任务的描述,具体实现看下面)
    private List<Node> list;
    //当前正在被监听的任务
    private Node current;
    //是否需要停止任务的状态
    private volatile boolean flag = true;
    //监听线程是否工作的状态
    private volatile boolean qd = true;
  1. 准备工作,Node类:对任务的描述类,StopHolder类:任务停止函数
class Node {
		//执行任务的线程
        Thread thread;
        //该任务的超时时间(时间戳)
        Long timeout;
        //停止任务的方法(接口,具体需要自己实现)
        StopHolder stopHolder;
}

@FunctionalInterface
public interface StopHolder {
    void stop();
}
  1. 初始化上面的变量,并启动监听线程
//int thread
    static {
        instance = new RunTimeoutUtil();
        instance.thread = new Thread(instance);
        instance.thread.setDaemon(true);
        instance.thread.setName("RUN-TIMEOUT-THREAD");
        instance.list = Collections.synchronizedList(new ArrayList<>());
        instance.thread.start();
    }
  1. 暴露出用户调用的公共方法,并开始监听和执行方法
/**
     * @param runnable: 被监听的任务函数
     * @param stopHolder: 任务停止函数
     * @param timeout:  执行任务的超时时间
     */
    public static void excutor(Runnable runnable, StopHolder stopHolder, Long timeout) {
        //添加监听事件
        instance.listener(stopHolder, timeout);
        try {
            //执行用户原本的方法
            runnable.run();
        } catch (Throwable e) {
            throw e;
        } finally {
        	//移除监听
            instance.remover();
        }
    }
  1. 监听任务:在多线程内,将已经超时的任务执行停止方法,由于会有多个任务,所以多个任务我们用循环来监听,通过将不同的任务超时时间的大小进行排序。监听完一个任务后,将监听下一个任务

java 查看各方法执行时间工具 java方法执行时间监控_工具类



所以代码实现大概就是:

@Override
    public void run() {
        while (true) {
            if (qd) {
                //没有任务时,将监听线程阻塞在此处
                LockSupport.park();
            }
            if (current != null) {
                // 有任务,线程阻塞,并在timeout后释放
                LockSupport.parkUntil(current.thread, current.timeout);
            }
            if (current == null) {
                qd = true;
            }
            if (flag) {
                //当前任务执行停止方法
                current.stopHolder.stop();
                //从队列中移除这个任务
                list.remove(0);
                if (list.size() > 0) {
                	//如果队列中还有任务,则将队列中的第一个任务设置为当前需要执行的任务
                    current = list.get(0);
                    //将无任务状态设置为false
                    qd = false;
                } else {
                    qd = true;
                    current = null;
                }
            }
        }
    }
  1. 添加监听:分两种情况,
    1.队列中没有任务,这时只要将当前任务设置为这次添加的任务,当前唤醒阻塞的监听线程
    2.队列中有任务,这种情况要复杂一些,需要将此次的任务添加到队列中,然后对队列进行排序,找到超时时间最近的那个任务,如果超时时间最近的任务还是正在监听的任务,则无需处理,若不是,则需要替换正在被监听的任务
private void listener(StopHolder stopHolder, Long timeout) {
        Node node = new Node();
        node.timeout = timeout + System.currentTimeMillis();
        node.thread = Thread.currentThread();
        node.stopHolder = stopHolder;
        if (current == null) {
            // no task is running
            this.current = node;
            this.list.add(node);
            this.qd = false;
            LockSupport.unpark(instance.thread);
        } else {
            this.list.add(node);
            // sort task
            Collections.sort(list, (node1, node2) -> (int) (node1.timeout - node2.timeout));
            if (list.get(0) != current) {
                // first task not currnet task
                flag = false;
                qd = false;
                current = null;
                LockSupport.unpark(instance.thread);
                while (!qd) {
                }
                current = list.get(0);
                flag = true;
                qd = false;
                LockSupport.unpark(instance.thread);
            }
        }
    }
  1. 原本任务执行完后的首位工作:不是所有的任务都会超过超时时间,所以需要将本源就正常完成的任务(没有执行过stop的任务)从队列中移除,并且开始对下一个任务的监听,具体代码如下:
private void remover() {
        if (current != null && current.thread == Thread.currentThread()) {
            // current thrad is task ,this task finished without timeout
            list.remove(list.get(0));
            flag = false;
            qd = false;
            current = null;
            LockSupport.unpark(instance.thread);
            if (list.size() > 0) {
                //start next task
                while (!qd) {
                }
                current = list.get(0);
                flag = true;
                qd = false;
                LockSupport.unpark(instance.thread);
            } else {
                current = null;
            }
        }
    }

至此,完毕