java多线程有两个重要的接口,runnable和callable,分别提供一个run方法和call方法,二者是有较大差异的。
1)runnable提供run方法,无法通过throws抛出异常,所有checkedexception必须在run方法内部处理。callable提供call方法,直接抛出exception异常。
2)runnable的run方法无返回值,callable的call方法提供返回值用来表示任务运行的结果
3)runnable可以作为thread构造器的参数,通过开启新的线程来执行,也可以通过线程池来执行。而callable只能通过线程池执行。
一、runnable使用场景
1)作为thread的构造参数开启新的线程,以下是常用的通过匿名内部类的方式创建线程。
thread thread = new thread(new runnable() { @override public void run() { system.out.println("i am a runnable task"); } }); thread.start();
2)由于java只提供单继承,故创建线程时一般通过实现runnable接口,来实现run方法的具体逻辑。然后实例化,作为thread的构造参数开启线程。
class runnabletask implements runnable { @override public void run() { system.out.println("i am a runnable task"); } }
main方法:
runnabletask runnabletask = new runnabletask(); thread thread1 = new thread(runnabletask); thread1.start();
其实1)和2)的本质是一样的。
3)作为线程任务提交给线程池,通过线程池维护的工作者线程来执行。
executorservice executor = executors.newcachedthreadpool(); runnabletask runnabletask = new runnabletask(); executor.execute(runnabletask); executor.shutdown();
二、callable的使用场景
因为callable的call方法提供返回值,所以当你需要知道任务执行的结果时,callable是个不错的选择。callable的实现很简单。
如下两个callable任务分别返回false和0到520整数之和。
class booleancallabletask implements callable<boolean> { @override public boolean call() throws exception { return false; } }
class integercallabletask implements callable<integer> { @override public integer call() throws exception { int sum = 0; for (int i = 0; i < 520; i++) { sum += i; } return sum; } }
callable任务通过线程池的submit方法提交。且submit方法返回future对象,通过future的get方法可以获得具体的计算结果。而且get是个阻塞的方法,如果任务未执行完,则一直等待。
executorservice executor = executors.newcachedthreadpool(); integercallabletask integercallabletask = new integercallabletask(); future<integer> future = executor.submit(integercallabletask); executor.shutdown(); try { system.out.println(future.get()); } catch (interruptedexception e) { e.printstacktrace(); } catch (executionexception e) { e.printstacktrace(); }
三、关于future和futuretask
对于calleble来说,future和futuretask均可以用来获取任务执行结果,不过future是个接口,futuretask是future的具体实现,而且futuretask还间接实现了runnable接口,也就是说futuretask可以作为runnable任务提交给线程池。
以下是个具体的实例演示futuretask各种的使用方式。
static class task implements callable<integer> { @override public integer call() throws exception { system.out.println("子线程在进行计算"); thread.sleep(1000); int sum = 0; for (int i = 0; i < 10000; i++) sum += i; return sum; } } public static void main(string[] args) throws interruptedexception { executorservice executor = executors.newcachedthreadpool(); //使用futuretask callable<integer> task = new task(); futuretask<integer> futuretask = new futuretask<integer>(task); executor.submit(futuretask); //使用future // callable<integer> call = new task(); // future<integer> future = executor.submit(call); executor.shutdown(); system.out.println("主线程在执行任务"); thread.sleep(2000); try { system.out.println("task运行结果" + futuretask.get()); //future.get() } catch (interruptedexception e) { e.printstacktrace(); } catch (executionexception e) { e.printstacktrace(); } system.out.println("所有任务执行完毕"); }
这个例子中使用future和futuretask都是一样的,都能获得相同的计算结果。
到此这篇关于java中runnable和callable的区别和联系的文章就介绍到这了,更多相关java runnable和callable的区别内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论