Worina008
Worina008
2011-09-01 11:38

关于使用线程池控制任务超时的问题

[size=large]实现一个在规定的时间内完成一个任务,使用线程池实现,如果在规定的时间内,没完成,即结束该线程任务。以下是我的代码:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class My {
private static ExecutorService executor = [b]Executors.newCachedThreadPool()[/b];//定义一个线程池
/**
* @param args
*/
public static void main(String[] args) {
Boolean result = false;
Future future = executor.submit(new MyJob());//将任务提交到线程池中
try {
result = future.get(200, TimeUnit.MILLISECONDS);//设定在200毫秒的时间内完成
} catch (InterruptedException e) {

        e.printStackTrace();
    } catch (ExecutionException e) {

        e.printStackTrace();
    } catch (TimeoutException e) {
        System.out.println("Time is out");//抛出超时异[size=xx-small][/size][size=large][/size]常时打印
    }
}

}

//业务job
import java.util.concurrent.Callable;
//实现Callable接口
public class MyJob implements [b]Callable[/b] {

@Override
public Boolean call() {
    //do job here
    long startTime = System.currentTimeMillis();
    for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时
    System.out.println("continue...");
    return true;
}

}

执行void函数时输出结果如下:
Time is out
continue...

这和我的要求不符,我原本是希望在抛出异常之后任务即结束,不再执行System.out.println("continue...");
请问如何解决[/size]

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

27条回答

  • weixin_42565855 weixin_42565855 10年前

    [quote]在这个job中的call方法应为有for()循环可以找到在哪中断线程,但是如果只是在连接数据库的话,那就无法知道执行到哪,应该加入中断线程的代码,这应该在外面控制,而不应该在job里面实现中断[/quote]

    线程的中断在JDK1.0版本中提供了stop()方法,但这个方法极度不安全,会不管
    当前对象的状态而直接释放所有的monitor,结束线程,会导致对象处于错误的状态。
    所以已经不被推荐使用。

    试想,有个任务,A向B转账10000¥,需要先从A账户上扣款10000¥,再将B账号加
    10000¥,如果A账号扣款完,这是从外面强行杀死线程,会导致什么后果?

    中止一个线程最好的方式就是使用中断,在外面通知线程中断,线程将中断状态设置
    为true,而具体是否要中断,中断时需要如何处理以保证数据状态是正确的,这些
    应交给任务自己处理。

    在什么地方控制中断,应该任务中控制,由你的业务逻辑决定,就算是
    操作数据库也一样可以设置中断点,或者说更应该判断中断点,来决定是否回滚
    事务。

    点赞 评论 复制链接分享
  • zzy7182 zzy7182 10年前

    String N= "";

    String M= "";

    String id= "";

    String pwd = "";

    protected void doPost(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException
    {

    this.M = req.getParameter("M").trim();
    this.N= req.getParameter("N").trim();
    this.id = req.getParameter("id").trim();
    this.pwd = req.getParameter("pwd").trim();
    

    /................../
    }

    当多个系统同时调用该servlet的时候,出现了servlet的非线程安全的问题。比如有两个系统同时调用该servlet,代码中的N、M、id、pwd的值会出现,一个线程的数据被另一个线程的数据“覆盖”的问题。
    [color=red] 我的问题是,第一:是否跟这几个变量是全部变量有关,如果设置为局部变量会出现这个问题吗?第二:使用多个servlet能解决这个问题,但是是最好的办法吗?[/color]

    点赞 评论 复制链接分享
  • sd_zyl sd_zyl 10年前

    楼主cancle地方不对,中断异常后返回也有问题,仔细看下面:
    [code="java"]
    public class Test {

    private static ExecutorService executor = Executors.newCachedThreadPool();// 定义一个线程池
    
    /**
     * @param args
     */
    public static void main(String[] args) {
        Boolean result = false;
        Future<Boolean> future = executor.submit(new MyJob());// 将任务提交到线程池中
        try {
            result = future.get(20, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
            // future.cancel(true);
        } catch (InterruptedException e) {
    
            e.printStackTrace();
        } catch (ExecutionException e) {
    
            e.printStackTrace();
        } catch (TimeoutException e) {
            System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
            //executor.shutdown();// 出现异常即可终止任务。
            [color=red]future.cancel(true);[/color]
        }
        //executor.shutdown();
        System.out.println(result);
    }
    

    }

    class MyJob implements Callable {

    @SuppressWarnings("static-access")
    @Override
    public Boolean call() {
        // do job here
    

    // long startTime = System.currentTimeMillis();
    // for (int i = 0; System.currentTimeMillis() - startTime < 2000; i++) {
    // System.out.println(i);
    // }
    try {
    Thread.currentThread().sleep(300);
    } catch (InterruptedException e) {
    e.printStackTrace();
    [color=red]return false; [/color]
    }
    // 模拟业务执行超过2000毫秒,即已经超时
    System.out.println("continue...");
    return true;
    }
    }
    [/code]

    点赞 评论 复制链接分享
  • iteye_16885 iteye_16885 10年前

    你对自己要做什么可能很清楚,但你对线程池能做什么确实很模糊,自己把自己绕晕了!!!
    回滚事务,如果超时事务回滚到操作以前的场景!

    点赞 评论 复制链接分享
  • u011606457 _1_1_7_ 10年前

    改了一下你原先的代码
    [code="java"]
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.TimeoutException;
    import java.util.concurrent.Callable;

    public class My {
    // 业务job

    // 实现Callable接口
    static class MyJob implements Callable<Boolean> {
    
        public Boolean call() {
            // do job here
            long startTime = System.currentTimeMillis();
            for (int i = 0; System.currentTimeMillis() - startTime < 2000; i++) {
                //测试当前线程是否已经中断,配合future.cancel(true)
                if (Thread.interrupted())
                    return false;
            }// 模拟业务执行超过2000毫秒,即已经超时
            System.out.println("continue...");
            return true;
        }
    }
    
    private static ExecutorService executor = Executors.newCachedThreadPool();// 定义一个线程池
    
    /**
     * @param args
     */
    public static void main(String[] args) {
        Boolean result = false;
        Future<Boolean> future = executor.submit(new MyJob());// 将任务提交到线程池中
        try {
            result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
        } catch (InterruptedException e) {
    
            e.printStackTrace();
        } catch (ExecutionException e) {
    
            e.printStackTrace();
        } catch (TimeoutException e) {// 超时异常
            System.out.println("Time is out");
            future.cancel(true);// 中断执行此任务的线程
        }
    }
    

    } [/code]

    测试通过,不会打印"continue..."

    点赞 评论 复制链接分享
  • itcastcom 赵子阳 10年前

    [code="java"]package dl.java.iteye;

    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.TimeoutException;
    import java.util.concurrent.Callable;

    public class My {
    private static ExecutorService executor = Executors.newCachedThreadPool();// 定义一个线程池
    /**
    * @param args
    */
    public static void main(String[] args) {
    Boolean result = false;
    MyJob job = new MyJob();
    Future future = executor.submit(job);// 将任务提交到线程池中
    try {
    result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
    } catch (InterruptedException e) {

            e.printStackTrace();
        } catch (ExecutionException e) {
    
            e.printStackTrace();
        } catch (TimeoutException e) {
            System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
            job.thread.stop();
        }
    }
    
    //业务job 
    //实现Callable接口 
    static class MyJob implements Callable<Boolean> {
       public Thread thread = null;
        @Override
        public Boolean call() throws Exception {
            //do job here 
            long startTime = System.currentTimeMillis(); //获取当前运行线程
            thread = Thread.currentThread();
            for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时 
            System.out.println("continue..."); 
            return true; 
        }
    }
    

    }

    [/code]

    这是一个案例,你根据实际情况,修改吧。你可以复制,运行一下。打印结果:

    Time is out

    点赞 评论 复制链接分享
  • itcastcom 赵子阳 10年前

    [quote]xiaoyang0601 写道
    一个这种的办法是获取MyJob当前的线程;在MyJob对象中声明一个变量thread
    Java代码

    import java.util.concurrent.Callable;

    //实现Callable接口

    public class MyJob implements Callable {

    public Thread thread = null; //定义一个当前任务线程
    @Override

    public Boolean call() throws Exception {

    //do job here

    long startTime = System.currentTimeMillis();

    thread = Thread.currentThread();//获取当前线程,,外界可以访问
    for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时

        System.out.println("continue...");    
    
        return true;    
    }   
    

    }

    import java.util.concurrent.Callable;
    //实现Callable接口
    public class MyJob implements Callable {
    [b] public Thread thread = null;[/b]

    @Override
    public Boolean call() throws Exception {
    //do job here
    long startTime = System.currentTimeMillis();
    [b]thread = Thread.currentThread();[/b]//获取当前线程 for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时

        System.out.println("continue..."); 
    
        return true; 
    }
    

    }

    在My对象中当超时,就对该工作任务的线程进行终止。
    Java代码

    Boolean result = false;

    MyJob job = new MyJob();//将提交的任务
    Future future = executor.submit(job);// 将任务提交到线程池中

    try {

    result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成

    // future.cancel(true);

    } catch (InterruptedException e) {

            e.printStackTrace();   
        } catch (ExecutionException e) {   
    
            e.printStackTrace();   
        } catch (TimeoutException e) {   
            System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印   
            job.thread.stop();//访问MyJob线程。
     }  
    

    Boolean result = false;
    [b]MyJob job = new MyJob();[/b] Future future = executor.submit(job);// 将任务提交到线程池中
    try {
    result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
    // future.cancel(true);
    } catch (InterruptedException e) {

            e.printStackTrace();
        } catch (ExecutionException e) {
    
            e.printStackTrace();
        } catch (TimeoutException e) {
            System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
            [b]job.thread.stop();[/b]       }
    

    我也想用stop(),但是job.thread取不到该线程啊

    [/quote]

    能访问到啊。你仔细看看代码。在MyJob中声明一个任务线程,这个任务线程就是当前任务的,在My对象中,声明一个MyJob job = new MyJob();
    Future future = executor.submit(job);//

    在超时异常代码中
    job.thread.stop();是可以访问的。你或许访问修饰符有误。
    还有你提的这个问题,中超时不再处理以后的任务,但这个超时的后的任务放在任务中,欠妥,可以提取出来,进行重构,先进性必要的超时处理,如果没有超时在My对象中进行处理业务,如果超时就不再处理。

    点赞 评论 复制链接分享
  • weixin_42565855 weixin_42565855 10年前

    一般涉及到网络连接的地方,都会提供超时接口,getConnection也不例外,
    不需要自己来控制超时。
    [code="java"]

    /**
    * Sets the maximum time in seconds that a driver will wait
    * while attempting to connect to a database.

    *
    * @param seconds the login time limit in seconds; zero means there is no limit
    * @see #getLoginTimeout
    */
    public static void setLoginTimeout(int seconds) {
    loginTimeout = seconds;
    }
    [/code]

    点赞 评论 复制链接分享
  • rainbow702 rainbow702 10年前

    [quote]
    难道就无解了吗?
    [/quote]
    暂时没想出来,晚上回去我有开发环境了,我再试试看

    点赞 评论 复制链接分享
  • rainbow702 rainbow702 10年前

    [quote]
    例子很好,不过要在任务中控制,不知道该如何在任务中设置中断点
    比如说简单的获取数据库connection,我要在规定时间内判断,若没连接成功就中断线程
    [/quote]
    因为你获得不到这个JOB所在的线程,所以你没有办法来中断它的。

    点赞 评论 复制链接分享
  • rainbow702 rainbow702 10年前

    [quote]我也想用stop()[/quote]
    stop() 已经是不推荐使用了,就算你获得了JOB所在的线程,请也别使用这个方法

    点赞 评论 复制链接分享
  • itcastcom 赵子阳 10年前

    一个这种的办法是获取MyJob当前的线程;在MyJob对象中声明一个变量thread
    [code="java"]import java.util.concurrent.Callable;
    //实现Callable接口
    public class MyJob implements Callable {
    [b] public Thread thread = null;[/b]

    @Override
    public Boolean call() throws Exception {
    //do job here
    long startTime = System.currentTimeMillis();
    [b]thread = Thread.currentThread();[/b]//获取当前线程 for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时

        System.out.println("continue..."); 
    
        return true; 
    }
    

    }[/code]

    在My对象中当超时,就对该工作任务的线程进行终止。
    [code="java"]Boolean result = false;
    [b]MyJob job = new MyJob();[/b] Future future = executor.submit(job);// 将任务提交到线程池中
    try {
    result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
    // future.cancel(true);
    } catch (InterruptedException e) {

            e.printStackTrace();
        } catch (ExecutionException e) {
    
            e.printStackTrace();
        } catch (TimeoutException e) {
            System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
            [b]job.thread.stop();[/b]       }[/code]
    
    点赞 评论 复制链接分享
  • iteye_9505 iteye_9505 10年前

    我习惯的做法是在工作线程内部设置一个boolean变量,如果外界想让线程结束就设置一下这个变量。在线程内部会判断这个变量,看是否要退出。

    点赞 评论 复制链接分享
  • weixin_42565855 weixin_42565855 10年前

    [quote]public class MyJob implements Callable {

    @Override
    public Boolean call() {
    //do job here
    long startTime = System.currentTimeMillis();
    for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时
    System.out.println("continue...");
    return true;
    }
    } [/quote]

    你这个测试任务写的有问题,根本没有控制中断,for循环会一直执行到结束。
    要在循环中加入线程中断的判断。
    Thread.currentThread().interrupted()

    [quote]public class MyJob implements Callable {

    @Override
    public Boolean call() {
    //do job here
    long startTime = System.currentTimeMillis();
    for(int i=0;System.currentTimeMillis()-startTime<2000;i++){
    if(Thread.currentThread().interrupted()){
    return false;
    }
    }//模拟业务执行超过2000毫秒,即已经超时
    System.out.println("continue...");
    return true;
    }
    } [/quote]

    在外面执行future.cancel(true)这时就能结束任务了。

    点赞 评论 复制链接分享
  • remotejsky remotejsky 10年前

    主要是要实现自己的终止策略
    比如在for循环中可以加入这样的判断
    [code="java"]
    for(;;){
    if(Thread.currentThread().isInterrupted())
    return false; //这就是来响应future.cancel啊,你不响应他,咋停啊
    }
    [/code]
    Java 并发编程实践这本书,看了基本上可以解决大部分问题了

    点赞 评论 复制链接分享
  • rainbow702 rainbow702 10年前

    [quote]使用thread能实现吗?还要有返回值的 [/quote]
    使用 Thread.sleep() 的话,你可以这么写:
    [code="java"]
    try{
    Thread.sleep(2000);
    } catch (InterruptedException e) {
    return false;
    }
    System.out.println("continue...");
    return true;
    [/code]

    点赞 评论 复制链接分享
  • jack_tianzhenhai Jack_tianzhenhai 10年前

    你能把你的for循环里面模仿超时的语句贴出来吗?我用的是Thread.sleep();

    点赞 评论 复制链接分享
  • rainbow702 rainbow702 10年前

    [quote]是不是使用线程池提交,线程池将任务分配给多个线程处理了,cancel()后仍然将任务分配给其他的线程执行?[/quote]这个需要看源码啊。但我认为,应该是针对每一个被submit上去的任务开一个线程来执行

    点赞 评论 复制链接分享
  • rainbow702 rainbow702 10年前

    [quote]res为true[/quote]
    呃,那这就奇怪了啊。明明任务已经被取消了,怎么还会打印出下面那句话呢。郁闷啊。。。

    点赞 评论 复制链接分享
  • jack_tianzhenhai Jack_tianzhenhai 10年前

    我验证过了在"time is out"的打印后面加future.cancel(true);是可行的,不知道你怎么验证的?
    } catch (TimeoutException e) {
    System.out.println("Time is out");//抛出超时异[size=xx-small][/size]常时打印
    future.cancel(true);
    }

    点赞 评论 复制链接分享
  • remotejsky remotejsky 10年前

    [code="java"]
    public class My {

    private static ExecutorService executor = Executors.newCachedThreadPool();// 定义一个线程池
    public static void main(String[] args) {
        Future<Boolean> future = executor.submit(new MyJob());// 将任务提交到线程池中
        executor.shutdown();  //关闭线程池 不允许再提交任务,并不是关闭线程池中的线程
        try {
            future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            future.cancel(true);//关闭超时的这个任务
            //executor.shutdownNow(); //因为这个任务超时,终止了所有的线程.
            System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
        }
    }
    
    /**
     * 
     * 任务。其实完全可以或应该在任务重控制自己何时超时,
     * 当然我顺着你的思路来了
     */
    public static class MyJob implements Callable<Boolean> {
    
        public Boolean call() throws Exception {
            long startTime = System.currentTimeMillis();
    
            try {
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
                return false;   //这里要响应future的中断啊,
                                //返回false吧,或者超时了抛个异常也行,这就是所谓的中断策略
                                //Java5以后的线程终止方法被废了,很多时候终止策略应该自己来实现
            }
            System.out.println("continue...");
            return true;
        }
    
    }
    

    }
    [/code]

    点赞 评论 复制链接分享
  • rainbow702 rainbow702 10年前

    [code="java"]
    catch (TimeoutException e) {
    System.out.println("Time is out");//抛出超时异常时打印
    boolean res = future.cancel(true);

    System.out.println("res:" + res);
    }
    [/code]
    你这么改一下,看看 res 的值是true 还是 false;

    点赞 评论 复制链接分享
  • itcastcom 赵子阳 10年前

    [code="java"]
    public class My {
    private static ExecutorService executor = Executors.newCachedThreadPool();// 定义一个线程池

    /**
     * @param args
     */
    public static void main(String[] args) {
        Boolean result = false;
        Future<Boolean> future = executor.submit(new MyJob());// 将任务提交到线程池中
        try {
            result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
    

    // future.cancel(true);
    } catch (InterruptedException e) {

            e.printStackTrace();
        } catch (ExecutionException e) {
    
            e.printStackTrace();
        } catch (TimeoutException e) {
            System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
            executor.shutdown();//出现异常即可终止任务。
        }
        executor.shutdown();
        System.out.println(result);
    }
    

    }
    [/code]

    需要调用 executor.shutdown();
    终止当前线程任务。

    点赞 评论 复制链接分享
  • remotejsky remotejsky 10年前

    在for循环后面抛个异常这样挺好哈
    if (System.currentTimeMillis() - startTime >=2000)
    throw new TimeoutException();
    或者在你的job里实现取消策略,在需要时自己取消。

    Future future = executor.submit(new MyJob());// 将任务提交到线程池中
    executor.shutdown();//没人提交了该shutdown()了吧

    点赞 评论 复制链接分享
  • rainbow702 rainbow702 10年前

    [quote]
    catch (TimeoutException e) {
    System.out.println("Time is out");//抛出超时异[size=xx-small][/size]常时打印
    }
    [/quote]
    在你的System.out.print()语句下,添加一行代码:
    [code="java"]
    future.cancel(true);
    [/code]
    看看行不行

    点赞 评论 复制链接分享
  • weixin_42565855 weixin_42565855 10年前

    在外面控制时间,200毫秒后,执行
    future.cancel(true);

    点赞 评论 复制链接分享
  • freishlove freishlove 10年前

    这好像没什么好办法

    future.get(200, TimeUnit.MILLISECONDS)这个只是等结果的时间

    点赞 评论 复制链接分享

为你推荐