java中限制方法运行时常的一个小工具类
- 说明
- 设计思路
- 开始编码
说明
由于是本人初次设计,肯定有很多bug,不建议大家使用,可以用来参考学习设计思路
监听java方法:
- 多线程实现监听 :创建一个守护线程,同时维护一个队列。将需要监听的方法放入队列中;
- 接口函数创建监听对象 :定义一个接口,将被监听的方法通过传参的方式传递到工具类中;
开始编码
- 准备工作,创建工具类,定义一个线程对象和队列,并设置一些后面需要用到的变量
类变量.
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;- 准备工作,Node类:对任务的描述类,StopHolder类:任务停止函数
class Node {
//执行任务的线程
Thread thread;
//该任务的超时时间(时间戳)
Long timeout;
//停止任务的方法(接口,具体需要自己实现)
StopHolder stopHolder;
}
@FunctionalInterface
public interface StopHolder {
void stop();
}- 初始化上面的变量,并启动监听线程
//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();
}- 暴露出用户调用的公共方法,并开始监听和执行方法
/**
* @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();
}
}- 监听任务:在多线程内,将已经超时的任务执行停止方法,由于会有多个任务,所以多个任务我们用循环来监听,通过将不同的任务超时时间的大小进行排序。监听完一个任务后,将监听下一个任务

所以代码实现大概就是:
@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.队列中没有任务,这时只要将当前任务设置为这次添加的任务,当前唤醒阻塞的监听线程
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);
}
}
}- 原本任务执行完后的首位工作:不是所有的任务都会超过超时时间,所以需要将本源就正常完成的任务(没有执行过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;
}
}
}至此,完毕
















