您的位置:首页 > 编程语言 > Java开发

Java 多线程设置线程超时时间之 Callable接口和Future接口 线程超时控制

2017-02-11 19:28 1011 查看
来源URL:http://blog.csdn.net/zlj526/article/details/40782027

Callable接口和Future接口介绍

        在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现。
Future接口是Java标准API的一部分,在java.util.concurrent包中。Future接口是Java线程Future模式的实现,可以来进行异步计算。

        Future模式可以这样来描述:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。就相当于下了一张订货单,一段时间后可以拿着提订单来提货,这期间可以干别的任何事情。其中Future 接口就是订货单,真正处理订单的是Executor类,它根据Future接口的要求来生产产品。

        Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果,也可以设置任务执行的超时时间。这个设置超时的方法就是实现Java程序执行超时的关键。

        Future接口是一个泛型接口,严格的格式应该是Future<V>,其中V代表了Future执行的任务返回值的类型。 Future接口的方法介绍如下:
boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true
boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true
get () throws InterruptedException, ExecutionException  等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出
CancellationException

get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException

         Future的实现类有java.util.concurrent.FutureTask<V>即 javax.swing.SwingWorker<T,V>。通常使用FutureTask来处理我们的任务。FutureTask类同时又实现了Runnable接口,所以可以直接提交给Executor执行。使用FutureTask实现超时执行的代码如下:

[java] view
plain copy

 





ExecutorService executor = Executors.newSingleThreadExecutor();  

FutureTask<String> future =  

       new FutureTask<String>(new Callable<String>() {//使用Callable接口作为构造参数  

         public String call() {  

           //真正的任务在这里执行,这里的返回值类型为String,可以为任意类型  

       }});  

executor.execute(future);  

//在这里可以做别的任何事情  

try {  

    result = future.get(5000, TimeUnit.MILLISECONDS); //取得结果,同时设置超时执行时间为5秒。同样可以用future.get(),不设置执行超时时间取得结果  

} catch (InterruptedException e) {  

    futureTask.cancel(true);  

} catch (ExecutionException e) {  

    futureTask.cancel(true);  

} catch (TimeoutException e) {  

    futureTask.cancel(true);  

} finally {  

    executor.shutdown();  

}  

       不直接构造Future对象,也可以使用ExecutorService.submit方法来获得Future对象,submit方法即支持以 Callable接口类型,也支持Runnable接口作为参数,具有很大的灵活性。使用示例如下:

[java] view
plain copy

 





ExecutorService executor = Executors.newSingleThreadExecutor();  

FutureTask<String> future = executor.submit(  

   new Callable<String>() {//使用Callable接口作为构造参数  

       public String call() {  

      //真正的任务在这里执行,这里的返回值类型为String,可以为任意类型  

   }});  

//在这里可以做别的任何事情  

//同上面取得结果的代码  

        利用Future接口实现程序执行超时大致用法就这么多,改天需要研究下Future接口的内部实现,特别是设定执行超时的实现。
转载自http://westyi.javaeye.com/blog/714935

例子和注意事项

例1:try catch在for循环之外

[java] view
plain copy

 





package testexception;  

  

import java.util.concurrent.Callable;  

import java.util.concurrent.ExecutionException;  

import java.util.concurrent.ExecutorService;  

import java.util.concurrent.Executors;  

import java.util.concurrent.FutureTask;  

import java.util.concurrent.TimeUnit;  

import java.util.concurrent.TimeoutException;  

  

  

public class FutureTaskTest{  

    public static void main(String[] args) {  

        ExecutorService executor = Executors.newSingleThreadExecutor();  

        FutureTask<String> futureTask =  

               new FutureTask<String>(new Callable<String>() {//使用Callable接口作为构造参数  

                 public String call() {  

                     try {  

                         for (int i = 0; i < 5; i++) {  

                             Thread.sleep(1000);  

                             System.out.println("------------------------"+i);  

                        }  

                    } catch (InterruptedException e) {  

                        System.out.println("InterruptedException1111111");  

                        e.printStackTrace();  

                    }  

                   //真正的任务在这里执行,这里的返回值类型为String,可以为任意类型  

                    return "call result";  

               }});  

        executor.execute(futureTask);  

        //在这里可以做别的任何事情  

        try {  

            String result = futureTask.get(3000, TimeUnit.MILLISECONDS);   

            //取得结果,同时设置超时执行时间为5秒。同样可以用future.get(),不设置执行超时时间取得结果  

            System.out.println("___________"+result+"_______________");  

        } catch (InterruptedException e) {  

            System.out.println("InterruptedException222222");  

            futureTask.cancel(true);  

        } catch (ExecutionException e) {  

            System.out.println("InterruptedException333333333");  

            futureTask.cancel(true);  

        } catch (TimeoutException e) {  

            System.out.println("!!!!!!!!Time out!!!!!!!!!!");  

            futureTask.cancel(true);  

        } finally {  

            executor.shutdown();  

        }  

    }  

}  

输出结果:
------------------------0

------------------------1
------------------------2

!!!!!!!!Time out!!!!!!!!!!

InterruptedException1111111

java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at testexception.FutureTaskTest$1.call(FutureTaskTest.java:20)
at testexception.FutureTaskTest$1.call(FutureTaskTest.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)

        说明:3秒之内没有执行完所以抛了TimeoutException异常,并取消了线程,可以看到后面就没有输出3,4了。注意这里call()函数中,try catch在for循环之外,这样执行结果是没有问题的,是我们想要的结果;但是,如果把try catch写在了for循环之内,结果会怎样呢,请看下例。另外,result值即为call()函数返回的值本例中为“call
result”。

例子2:try catch在for循环之内

[java] view
plain copy

 





package testexception;  

  

import java.util.concurrent.Callable;  

import java.util.concurrent.ExecutionException;  

import java.util.concurrent.ExecutorService;  

import java.util.concurrent.Executors;  

import java.util.concurrent.FutureTask;  

import java.util.concurrent.TimeUnit;  

import java.util.concurrent.TimeoutException;  

  

  

public class FutureTaskTest{  

    public static void main(String[] args) {  

        ExecutorService executor = Executors.newSingleThreadExecutor();  

        FutureTask<String> futureTask =  

               new FutureTask<String>(new Callable<String>() {//使用Callable接口作为构造参数  

                 public String call() {  

                     for (int i = 0; i < 5; i++) {  

                         try {  

                                 Thread.sleep(1000);  

                                 System.out.println("------------------------"+i);  

                            } catch (InterruptedException e) {  

                                System.out.println("InterruptedException1111111");  

                                e.printStackTrace();  

                            }  

                     }  

                   //真正的任务在这里执行,这里的返回值类型为String,可以为任意类型  

                    return "call result";  

               }});  

        executor.execute(futureTask);  

        //在这里可以做别的任何事情  

        try {  

            String result = futureTask.get(3000, TimeUnit.MILLISECONDS);   

            //取得结果,同时设置超时执行时间为5秒。同样可以用future.get(),不设置执行超时时间取得结果  

            System.out.println("___________"+result+"_______________");  

        } catch (InterruptedException e) {  

            System.out.println("InterruptedException222222");  

            futureTask.cancel(true);  

        } catch (ExecutionException e) {  

            System.out.println("InterruptedException333333333");  

            futureTask.cancel(true);  

        } catch (TimeoutException e) {  

            System.out.println("!!!!!!!!Time out!!!!!!!!!!");  

            futureTask.cancel(true);  

        } finally {  

            executor.shutdown();  

        }  

    }  

}  

输出结果:

------------------------0

------------------------1

------------------------2

java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)

!!!!!!!!Time out!!!!!!!!!!

InterruptedException1111111
at testexception.FutureTaskTest$1.call(FutureTaskTest.java:20)
at testexception.FutureTaskTest$1.call(FutureTaskTest.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)

------------------------4

        说明:根据输出结果可以看到只有线程3被中断了,后面线程并没有按照我们希望的超时之后就停下来,即使我们在超时异常里面取消了线程。也就是说,try catch在for循环之内时,取消线程后会让线程中断,所以第四次循环未执行完即被中断,抛出中断异常,然而这个中断异常被for循环之内的线程中断异常catch捕获到,所以它仍会继续后面的循环输出。但其实现在的线程已经被标识为中断了,如果你在超时异常catch块中输出futureTask.isCancelled(),你会发现返回的是true。使用时要特别注意这种容易出错的情况。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐