java - 绑定(bind)线程运行时间

标签 java multithreading threadpool

我正在尝试查找有关如何限制使用ThreadPoolExecutor创建的任务的运行时间的更多信息。 .

我想创建一个自毁的,例如当时间过去(例如 1m)时,线程将自动终止并返回 null 值。这里的关键点是等待线程完成不应阻塞主线程(在我们的示例中为 UI 线程)。

我知道我可以使用 get方法,但是它会阻止我的应用程序。

我正在考虑运行一个额外的内部线程,该线程将 hibernate 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() 通过线程池调用

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来记录每个线程的启动时间。 beforeExecute 方法填充它,afterExecute 方法清除它。有一个 TimerTask 定期执行并查看所有当前条目(即所有正在运行的线程),并对所有超出给定时间限制的条目调用 Thread.interrupt()。

请注意,我给出了两个额外的构造函数参数:maxExecutionTime 和 reaperInterval 来控制给出任务的时间长度以及检查要终止的任务的频率。为了简洁起见,我在这里省略了一些构造函数。

请记住,您提交的任务必须表现良好并允许自己被杀死。这意味着您必须:

  1. 定期检查 Thread.currentThread().isInterrupted() 执行期间。
  2. 尽量避免任何未声明的阻塞操作 它的 throws 子句中存在 InterruptedException。一个典型的例子 将是InputStream/OutputStream用法,并且您将使用NIO channel 代替。如果必须使用这些方法,请在从此类操作返回后立即检查中断标志。

.

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);
        }
    }



}

关于java - 绑定(bind)线程运行时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10770317/

相关文章:

java - 找不到 jdeps 命令 (Mac)

java - Google Vision API JSON 响应仅提供英文版本

java - 如果您将对象分配给最终字段,其他线程是否会看到该对象的非最终/非 volatile 字段的先前更新?

java - ArrayBlockingQueue add 方法是即时的吗?

java - Camel 线程池查询

c# - 速度数据处理 : ThreadPool or Thread loop with event?

Java ZipFileSystem 属性

java - Spring 如何映射 URL 以及在哪里阅读更多相关信息

java - 如何在不访问上下文的情况下从线程更新我的 Android SQLite 数据库?

java - 使用 ThreadPool 并行化矩阵乘法