Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >(八)Callable和Runnable的区别

(八)Callable和Runnable的区别

作者头像
HaC
发布于 2020-12-30 09:57:07
发布于 2020-12-30 09:57:07
46400
代码可运行
举报
文章被收录于专栏:HaC的技术专栏HaC的技术专栏
运行总次数:0
代码可运行

Callable和Runnable都是一个接口。

Runnable

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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()函数。

简单使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 Thread thread =new Thread(new TestRunnable());
 thread.start();

Runnable的run方法没有返回值。

demo:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);
        }
    }
}

输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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方法有一个返回值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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();
    }
}

输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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封装在ExecutionException中重新抛出。

所以,无论是抛出的未检查异常还是已检查异常,都将被认为是任务返回状态的一部分,因此不会交由异常处理器来处理,我们就不需要使用ThreadFactory去处理异常了。

  1. 通过 feture.get() 的方法,一个线程结束后,就会空闲,所以虽然 corePoolSize+workQueue>=maximumPoolSize ,但也不会走拒绝策略。

以上结论:

  1. 通过execute方式提交的任务,能将它抛出的异常交给异常处理器。
  2. 通过submit方式提交任务,则异常不能被异常处理器捕获。

submit 和 execute的区别

  1. execute执行的是一个Runnable任务,submit 执行 Runnable和Callable都可以。
  2. execute的提交没有返回值,而submit的提交会返回一个Future类型的对象
  3. execute提交的时候,如果有异常,就会直接抛出异常;而submit在遇到异常的时候,通常不会立马抛出异常,而是会将异常暂时存储起来,等待你调用Future.get()方法的时候,才会抛出异常。

总结

  1. Runnable可以提交给Thread,直接启动一个线程来执行。核心方法是run(),通过excuse()方法执行。
  2. Callable一般都是提交给ExecuteService来执行,核心方法是call(),通过submit方法执行。
  3. Runnable执行没有返回;Callable有返回。

参考:

详解线程池execute和submit用法:

https://www.cnblogs.com/by-my-blog/p/10779333.html

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/07/25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
(九)线程池异常捕获
上一篇提到了使用ThreadFactory的UncaughtExceptionHandler去捕获线程池的错误,还有没有其他方法呢?
HaC
2020/12/30
1.7K0
21.3 Java 线程池
线程是在一个进程中可以执行一系列指令的执行环境,或称运行程序。多线程编程指的是用多个线程并行执行多个任务。当然,JVM 对多线程有良好的支持。
acc8226
2022/05/17
3740
21.3 Java 线程池
线程的创建、Lambda函数式接口?Runnable和Callable之间的适配?动态修改线程任务?这里带你图解Java线程池
这里为了便于叙述,毕竟不是本次的重点,我直接上源码,没基础的可以去找些其他资料来补一补
Karos
2023/08/11
9190
线程的创建、Lambda函数式接口?Runnable和Callable之间的适配?动态修改线程任务?这里带你图解Java线程池
Java中的Runnable、Callable、Future、FutureTask的区别
Java中存在Runnable、Callable、Future、FutureTask这几个与线程相关的类或者接口,在Java中也是比较重要的几个概念,我们通过下面的简单示例来了解一下它们的作用于区别。
开发者技术前线
2020/11/23
4700
Java的Executor框架和线程池实现原理
Executor接口是Executor框架中最基础的部分,定义了一个用于执行Runnable的execute方法,它没有实现类只有另一个重要的子接口ExecutorService
全栈程序员站长
2022/11/17
4780
Java的Executor框架和线程池实现原理
(六)ThreadPoolExecutor自定义线程池
上一篇中提到四种线程池的创建方式,最后还是会 new ThreadPoolExecutor(),所以 我们可以使用 new ThreadPoolExecutor()的方法创建自定义的线程。
HaC
2020/12/30
3.4K0
创建线程都有哪些方式?— Callable篇
相信大家回答这个问题没什么难度吧?通常问完创建方式,那么接下来就是问「1、2」跟「3」创建方式的不同了,只要说出「3」有返回值基本这个问题就过了,不管是出于好奇还是疑惑,我们今天来会会这个Callable。
niceyoo
2020/07/26
8010
Runnable和Callable源码及应用解析
里面使用Runnable的地方只有传递对象的时候,其他都是使用的Thread而Thread又实现了我们的Runnable,所以Runbale可以理解为执行代码的对象,执行的过程和线程的操作交由Thread控制,Thread源码可以看 ----》Thread源码解析。
余生大大
2022/11/02
2000
Runnable和Callable源码及应用解析
Java多线程学习(八)线程池与Executor 框架
Java面试通关手册(Java学习指南,欢迎Star,会一直完善下去,欢迎建议和指导):https://github.com/Snailclimb/Java_Guide
用户2164320
2018/07/08
1.1K0
Java多线程学习(八)线程池与Executor 框架
面试-线程池的成长之路
线程池是一种多线程处理形式,处理过程中将任务提交到线程池,任务的执行交由线程池来管理。
猿天地
2018/07/25
6480
Java多线程编程:Callable、Future和FutureTask浅析(多线程编程之四)「建议收藏」
java多线程-概念&创建启动&中断&守护线程&优先级&线程状态( 多线程编程之一) java多线程同步以及线程间通信详解&消费者生产者模式&死锁&Thread.join()( 多线程编程之二) java&android线程池-Executor框架之ThreadPoolExcutor&ScheduledThreadPoolExecutor浅析(多线程编程之三) Java多线程:Callable、Future和FutureTask浅析(多线程编程之四)
全栈程序员站长
2022/07/04
3020
Java多线程编程:Callable、Future和FutureTask浅析(多线程编程之四)「建议收藏」
java(7)-多线程和线程池
用Thread类的方式创建多线程的特点: 1、因为线程已经继承Thread类,所以不可以再继承其它类。 2、如果需要访问当前线程,直接使用this即可。
黄规速
2022/04/14
5110
java(7)-多线程和线程池
Executor框架
在HotSpot VM的线程模型中,Java线程(java.lang.Thread)被 一对一映射为本地操作系统线程。Java线程启动时会创建一个本地操作系统线程;当该Java线程终止时,这个操作系统线程也会被回收。 操作系统会调度所有线程并将它们分配给可用的CPU。 在上层,Java多线程程序通常把应用分解为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程;在底层,操作系统内核将这些线程映射到硬件处理器上。这种两级调度模型的示意图下面有介绍。 从下图中可以看出,应用程序通过Executor框架控制上层的调度;而下层的调度由操作系统内核控制,下层的调度不受应用程序的控制。
103style
2022/12/19
1880
Executor框架
Future和Callable学习
我们知道使用多线程时,最初的Thread到线程池,此时对于线程的使用,提供了其使用的复用率。而实现多线程的三种方式:继承Thread;实现Runnable接口,重写run方法;实现Callable接口,同时重写call方法,同时通过Future获取执行的返回值。也就是说callable执行任务,而Future拿到执行的结果。Future具有阻塞性在于其get()方法具有阻塞性,而isDone()是不具有阻塞性的。
路行的亚洲
2020/07/16
5050
Java线程池原理讲解
  线程池应该是Web容器中必不可少的组件了,因为每一个请求我们都需要通过对应的线程来处理,所以线程资源是非常重要的,如果管理不好系统的性能会急剧下降。所以重要性不言而喻。来看看它的有点吧。
用户4919348
2022/04/13
5860
Java线程池原理讲解
JUC线程池扩展可回调的Future
最近在看JUC线程池java.util.concurrent.ThreadPoolExecutor的源码实现,其中了解到java.util.concurrent.Future的实现原理。从目前java.util.concurrent.Future的实现来看,虽然实现了异步提交任务,但是任务结果的获取过程需要主动调用Future#get()或者Future#get(long timeout, TimeUnit unit),而前者是阻塞的,后者在异步任务执行时间不确定的情况下有可能需要进行轮询,这两种情况和异步调用的初衷有点相违背。于是笔者想结合目前了解到的Future实现原理的前提下扩展出支持(监听)回调的Future,思路上参考了Guava增强的ListenableFuture。本文编写的时候使用的JDK是JDK11,代码可以在JDK[8,12]版本上运行,其他版本可能不适合。
Throwable
2020/06/23
8380
Java并发编程--ThreadPoolExecutor
  合理利用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。但是要做到合理的利用线程池,必须对其原理了如指掌。——摘自http://www.infoq.com/cn/articles/java-threadPool。
在周末
2019/08/26
7820
Java并发指南12:深度解读 java 线程池设计思想及源码实现
本文是微信公众号【Java技术江湖】的《Java并发指南》其中一篇,本文大部分内容来源于网络,为了把本文主题讲得清晰透彻,也整合了很多我认为不错的技术博客内容,引用其中了一些比较好的博客文章,如有侵权,请联系作者。
Java技术江湖
2019/11/20
6410
Java并发指南12:深度解读 java 线程池设计思想及源码实现
Java-多线程框架Executor
在Java中,使用线程来异步执行任务。Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源。同时,为每一个任务创建一个新线程来执行,这种策略可能会使处于高负荷状态的应用最终崩溃。
小小工匠
2021/08/17
5050
Java Review - 线程池使用FutureTask的小坑
线程池使用FutureTask时如果把拒绝策略设置为 DiscardPolicy和 DiscardOldestPolicy,并且在被拒绝的任务的Future对象上调用了无参get方法,那么调用线程会一直被阻塞。
小小工匠
2021/11/22
5060
Java Review - 线程池使用FutureTask的小坑
相关推荐
(九)线程池异常捕获
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验