使用超时在java中运行异步代码

时间:2015-01-23 10:39:02

标签: java multithreading futuretask

在我写的网络服务器中,每个请求都会调用一个动作列表。其中一些操作并不像其他操作那么重要,所以我想在后台线程中运行它们。

另外,由于它们并不重要,我不在乎其中一个是否很少失败,我不希望它们永远占用一个线程,因此其他线程可用于处理下一批。

所以,我想有一个线程池(例如:10个线程),并像这样向每个后台任务分发一个线程。将每个线程限制为1秒,如果它在那段时间内没有完成,只需将其删除,然后可以进入下一个任务。

我将如何做到这一点?

到目前为止,这就是我所拥有的:

public class AsyncCodeRunner {

    private static final ExecutorService executor = Executors.newFixedThreadPool(10);

    public void Run(Callable<Void> callableCode, int timeout) {

        final int threadTimeout = 10;
        Future<Void> callableFuture = executor.submit(callableCode);

        try {
            callableFuture.get(threadTimeout, TimeUnit.SECONDS);
        } catch (Exception e) {
            logger.Info("Thread was timed out", e);
        }
    }
}

我想像这样使用这个类:

public void processRequest(RequestObject request) {

    // do some important processing

    // throw some less important processing to background thread
    (new AsyncCodeRunner()).Run(new Callable<Void> () {
        @Override
        public Void call() throws Exception {
            // do something...
            return null;
        }
    }, 1); // 1 second timeout

    // return result (without waiting for background task)
    return;

}

这会像我想要的那样工作吗?或者我应该如何改变呢? 如果我调用Run()但线程池中没有可用的线程可以分发,会发生什么?

2 个答案:

答案 0 :(得分:1)

我认为这个相当优雅的想法的主要问题是你只是在get的{​​{1}}超时,一旦超时,你实际上并没有中止这个过程,你只是放弃等待它。当你意识到你甚至可能在进程还没有开始时超时时,问题会变得更加复杂 - 它仍然在队列中。

也许这样的事情会有效。它确实需要两个线程,但Future线程应该消耗很少。

TimerTask

这是一种只使用一个额外线程的替代方案。

public class RunWithTimeout {

    public RunWithTimeout(Runnable r, long timeout) {
        // Prepare the thread.
        final Thread t = new Thread(r);
        // Start the timer.
        new Timer(true).schedule(new TimerTask() {

            @Override
            public void run() {
                if (t.isAlive()) {
                    // Abort the thread.
                    t.interrupt();
                }
            }
        }, timeout * 1000);
        // Start the thread.
        t.start();
    }

}

class WaitAFewSeconds implements Runnable {

    final long seconds;

    WaitAFewSeconds(long seconds) {
        this.seconds = seconds;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(seconds * 1000);
        } catch (InterruptedException ie) {
            System.out.println("WaitAFewSeconds(" + seconds + ") - Interrupted!");
        }
    }

}

public void test() {
    new RunWithTimeout(new WaitAFewSeconds(5), 3);
    new RunWithTimeout(new WaitAFewSeconds(3), 5);
}

答案 1 :(得分:0)

您需要在线程运行时启动计时器。这样就不会杀死处于等待状态的线程。以下是this thread的示例:

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class PoolTest {
    class TimeOutTask extends TimerTask {
        Thread t;

        TimeOutTask(Thread t) {
            this.t = t;
        }

        public void run() {
            if (t != null && t.isAlive()) {
                t.interrupt();
            }
        }
    }

    class MyRunnable implements Runnable {
        Timer timer = new Timer(true);

        public void run() {
            timer.schedule(new TimeOutTask(Thread.currentThread()), 1000);
            try {
                System.out.println("MyRunnable...");
                Thread.sleep(10000);
            } catch (InterruptedException ie) {
                System.out.println("MyRunnable error...");
                ie.printStackTrace();
            }
        }
    }

    public static void main(String args[]) {
        new PoolTest();
    }

    public PoolTest() {
        try {
            ExecutorService pe = Executors.newFixedThreadPool(3);
            pe.execute(new MyRunnable());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}