(八)Callable和Runnable的区别
Callable和Runnable都是一个接口。
# Runnable
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
Runnable只有一个run方法,在使用普通线程的时候,我们可以实现Runnable接口即可,Thread类在调用start()函数后就是执行的是Runnable的run()函数。
简单使用:
Thread thread =new Thread(new TestRunnable());
thread.start();
Runnable的run方法没有返回值。
demo:
public class ThreadPoolALL {
public static void main(String[] args) {
new ThreadPoolALL().ExecutorThreadPool();
}
static class TestRunnable implements Runnable {
static int i = 0;
@Override
public void run() {
//加锁顺序执行
synchronized (this){
int count = getCount();
System.out.println(Thread.currentThread().getName() + " 线程被调用了。第" + count + "次");
if (count == 10) {
throw new RuntimeException("Runnable 任务出错了");
}
}
}
public static int getCount() {
return ++i;
}
}
public void ExecutorThreadPool() {
//自定义线程池
int corePoolSize = 2; //线程池维护线程的最少数量
int maxPoolSize = 3; //线程池维护线程的最大数量
long keepAliveTime = 60; //线程池维护线程所允许的空闲时间(解释:当线程池的数量超过corePoolSize时,多余的空闲线程的存活时间。)
RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>(10);
//或者
ThreadFactory factory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
SecurityManager s = System.getSecurityManager();
ThreadGroup group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
Thread t = new Thread(group, r);
//设置优先级
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
//设置错误处理
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
//自定义处理错误
System.out.println("factory捕获了错误--->>>" + t.getName() + e);
}
});
return t;
}
};
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, queue, factory, rejectedExecutionHandler);
TestRunnable testRunnable = new TestRunnable();
for (int i = 0; i < 10; i++) {
executor.execute(testRunnable);
}
}
}
输出:
Thread-0 线程被调用了。第1次
Thread-1 线程被调用了。第2次
Thread-0 线程被调用了。第3次
Thread-1 线程被调用了。第4次
Thread-0 线程被调用了。第5次
Thread-0 线程被调用了。第6次
Thread-0 线程被调用了。第7次
Thread-0 线程被调用了。第8次
Thread-0 线程被调用了。第9次
Thread-1 线程被调用了。第10次
factory捕获了错误--->>>Thread-1java.lang.RuntimeException: Runnable 任务出错了
可以看到factory可以自定义使用自己的异常捕获方法。
# Callable
Callable和Runnable最大的区别就是它的call方法有一个返回值。
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
说到返回值,又不得不提 Future。
Future就是对于具体Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作,其中get()方法会阻塞,所以通过get()方法可以判断线程是否执行完毕。
在Future接口中声明了5个方法,下面依次解释每个方法的作用:
cancel()
方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。isCancelled()
方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。isDone()
方法表示任务是否已经完成,若任务完成,则返回true;get()
方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;get(long timeout, TimeUnit unit)
用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
也就是说Future提供了三种功能:
1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。
demo:
public class ThreadPoolALL2 {
public static void main(String[] args) {
new ThreadPoolALL2().ExecutorThreadPool();
}
static class TestCallable implements Callable<String> {
static int i = 0;
public static int getCount() {
return ++i;
}
//可定义回参类型
@Override
public String call() {
int count = getCount();
System.out.println(Thread.currentThread().getName() + " 线程被调用了。第" + count + "次");
if (count == 2) {
throw new RuntimeException("Callable 任务出错了");
}
return "finish";
}
}
public void ExecutorThreadPool() {
//自定义线程池
int corePoolSize = 2; //线程池维护线程的最少数量
int maxPoolSize = 3; //线程池维护线程的最大数量
long keepAliveTime = 60; //线程池维护线程所允许的空闲时间(解释:当线程池的数量超过corePoolSize时,多余的空闲线程的存活时间。)
RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>(10);
//或者
ThreadFactory factory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
SecurityManager s = System.getSecurityManager();
ThreadGroup group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
Thread t = new Thread(group, r);
//设置优先级
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
//设置错误处理
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
//自定义处理错误
System.out.println("factory捕获了错误--->>>" + t.getName() + e);
}
});
return t;
}
};
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, queue, factory, rejectedExecutionHandler);
TestCallable testCallable = new TestCallable();
for (int i = 0; i < 2; i++) {
Future<String> future = executor.submit(testCallable);
try {
future.get();
String s = future.get();
if ("finish".equals(s)) {
System.out.println("future.isDone()的值:" + future.isDone() + ",经过返回值比较,submit方法执行任务成功,当前线程名称--->>>" + Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executor.shutdown();
}
}
输出:
Thread-0 线程被调用了。第1次
future.isDone()的值:true,经过返回值比较,submit方法执行任务成功,当前线程名称--->>>main
Thread-2 线程被调用了。第2次
java.util.concurrent.ExecutionException: java.lang.RuntimeException: Callable 任务出错了
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.yudianxx.basic.线程.Executor.ThreadPoolALL.ExecutorThreadPool(ThreadPoolALL.java:116)
at com.yudianxx.basic.线程.Executor.ThreadPoolALL.main(ThreadPoolALL.java:12)
Caused by: java.lang.RuntimeException: Callable 任务出错了
at com.yudianxx.basic.线程.Executor.ThreadPoolALL$TestCallable.call(ThreadPoolALL.java:48)
at com.yudianxx.basic.线程.Executor.ThreadPoolALL$TestCallable.call(ThreadPoolALL.java:35)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
可以看到:
future.get()
方法造成的阻塞,主线程等待子线程执行完毕。可以看到:
if ("finish".equals(s)) { System.out.println("future.isDone()的值:" + future.isDone() + ",经过返回值比较,submit方法执行任务成功,当前线程名称--->>>" + Thread.currentThread().getName()); }
是主线程main去执行的。
通过submit提交的任务,异常不会被ThreadFactory捕获,而是直接抛出到主线程抛出了。根本原因是
feture.get()
这里面,V get() throws InterruptedException, ExecutionException;
这个异常将被Future.get封装在ExecutionException中重新抛出。
所以,无论是抛出的未检查异常还是已检查异常,都将被认为是任务返回状态的一部分,因此不会交由异常处理器来处理,我们就不需要使用ThreadFactory去处理异常了。
- 通过
feture.get()
的方法,一个线程结束后,就会空闲,所以虽然corePoolSize+workQueue>=maximumPoolSize
,但也不会走拒绝策略。
# 以上结论:
- 通过execute方式提交的任务,能将它抛出的异常交给异常处理器。
- 通过submit方式提交任务,则异常不能被异常处理器捕获。
# submit 和 execute的区别
execute执行的是一个Runnable任务,submit 执行 Runnable和Callable都可以。
execute的提交没有返回值,而submit的提交会返回一个Future类型的对象
execute提交的时候,如果有异常,就会直接抛出异常;而submit在遇到异常的时候,通常不会立马抛出异常,而是会将异常暂时存储起来,等待你调用Future.get()方法的时候,才会抛出异常。
# 总结
- Runnable可以提交给Thread,直接启动一个线程来执行。核心方法是run(),通过excuse()方法执行。
- Callable一般都是提交给ExecuteService来执行,核心方法是call(),通过submit方法执行。
- Runnable执行没有返回;Callable有返回。
参考:
详解线程池execute和submit用法:
https://www.cnblogs.com/by-my-blog/p/10779333.html (opens new window)