我正在尝试查找有关如何绑定使用ThreadPoolExecutor创建的任务的运行时间的更多信息。
我想创建一个自毁,例如,当时间过去(例如 1m)时,线程将自动终止并返回 null 值。这里的关键点是等待线程完成不应阻塞主线程(在我们的示例中为 UI 线程)。
我知道我可以使用 get 方法,但它会阻止我的应用程序。
我正在考虑运行一个额外的内部线程,该线程将休眠 1m,然后在主线程上调用中断。
我附上了一个示例代码,它看起来是个好主意,但我需要另一双眼睛告诉我它是否有意义。
public abstract class AbstractTask<T> implements Callable<T> {
private final class StopRunningThread implements Runnable {
/**
* Holds the main thread to interrupt. Cannot be null.
*/
private final Thread mMain;
public StopRunningThread(final Thread main) {
mMain = main;
}
@Override
public void run() {
try {
Thread.sleep(60 * 1000);
// Stop it.
mMain.interrupt();
} catch (final InterruptedException exception) {
// Ignore.
}
}
}
call() 通过 ThreadPool 调用
public T call() {
try {
// Before running any task initialize the result so that the user
// won't
// think he/she has something.
mResult = null;
mException = null;
// Stop running thread.
mStopThread = new Thread(new StopRunningThread(
Thread.currentThread()));
mStopThread.start();
mResult = execute(); <-- A subclass implements this one
} catch (final Exception e) {
// An error occurred, ignore any result.
mResult = null;
mException = e;
// Log it.
Ln.e(e);
}
// In case it's out of memory do a special catch.
catch (final OutOfMemoryError e) {
// An error occurred, ignore any result.
mResult = null;
mException = new UncheckedException(e);
// Log it.
Ln.e(e);
} finally {
// Stop counting.
mStopThread.interrupt();
}
return mResult;
}
有几点我害怕:
- 如果 execute() 有一个异常,然后我的外部线程会立即中断,那么我永远不会捕捉到异常,会发生什么。
- 内存/CPU 消耗,我正在使用线程池来避免创建新线程。
您看到实现相同功能的更好主意吗?
这样做会有些复杂。 首先,您需要扩展 ThreadPoolExecutor 类。 您需要覆盖"beforeExecute"和"afterExecute"方法。 他们会跟踪线程开始时间,并在之后进行清理。 然后,您需要一个收割者定期检查以查看哪些线程需要清理。
此示例使用 Map 来记录每个线程的启动时间。 方法填充它,之后执行方法清理它。 有一个 TimerTask 定期执行并查看所有当前条目(即所有正在运行的线程),并在所有超过给定时间限制的条目上调用 Thread.interrupt()。
请注意,我给出了两个额外的构造函数参数:maxExecutionTime 和 reaperInterval,用于控制给定任务的时间,以及检查要终止的任务的频率。 为了简洁起见,我在这里省略了一些构造函数。
请记住,您提交的任务必须玩得很好,并允许自己被杀死。 这意味着您必须:
- 定期检查 Thread.currentThread().isInterrupted()在执行期间。
- 尽量避免任何未声明的阻塞操作InterruptedException 在其抛出子句中。 一个典型的例子将是 InputStream/OutputStream 用法,您将使用 NIO频道代替。 如果必须使用这些方法,请在从此类操作返回后立即检查中断标志。
.
public class TimedThreadPoolExecutor extends ThreadPoolExecutor {
private Map<Thread, Long> threads = new HashMap<Thread, Long>();
private Timer timer;
public TimedThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,
long maxExecutionTime,
long reaperInterval) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
startReaper(maxExecutionTime, reaperInterval);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
threads.remove(Thread.currentThread());
System.out.println("after: " + Thread.currentThread().getName());
super.afterExecute(r, t);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
System.out.println("before: " + t.getName());
threads.put(t, System.currentTimeMillis());
}
@Override
protected void terminated() {
if (timer != null) {
timer.cancel();
}
super.terminated();
}
private void startReaper(final long maxExecutionTime, long reaperInterval) {
timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
// make a copy to avoid concurrency issues.
List<Map.Entry<Thread, Long>> entries =
new ArrayList<Map.Entry<Thread, Long>>(threads.entrySet());
for (Map.Entry<Thread, Long> entry : entries) {
Thread thread = entry.getKey();
long start = entry.getValue();
if (System.currentTimeMillis() - start > maxExecutionTime) {
System.out.println("interrupting thread : " + thread.getName());
thread.interrupt();
}
}
}
};
timer.schedule(timerTask, reaperInterval, reaperInterval);
}
public static void main(String args[]) throws Exception {
TimedThreadPoolExecutor executor = new TimedThreadPoolExecutor(5,5, 1000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(20),
1000L,
200L);
for (int i=0;i<10;i++) {
executor.execute(new Runnable() {
public void run() {
try {
Thread.sleep(5000L);
}
catch (InterruptedException e) {
}
}
});
}
executor.shutdown();
while (! executor.isTerminated()) {
executor.awaitTermination(1000L, TimeUnit.MILLISECONDS);
}
}
}