Runnable与Callable 详解

Runnable与Callable区别

  • Callable规定的方法是 call(), Runnable规定的方法是 run()。
  • Callable的任务执行后可返回值,而 Runnable的任务是不能返回值。
  • call方法可以抛出异常, run方法不可以。
  • 运行 Callable任务可以拿到一个 Future对象
    *Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get方法来获取结果,如果线程没有执行完, Future.get()方法可能会阻塞当前线程的执行;如果线程出现异常, Future.get()会 throws InterruptedException或者 ExecutionException;如果线程已经取消,会跑出 CancellationException。取消由 cancel方法来执行。
    isDone确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future但又不提供可用的结果,则可以声明 Future

什么是Future?

  Future模式在请求发生时,会先产生一个 Future凭证给发出请求的客户,它的作用就像是 Proxy物件,同时,由一个新的执行线程持续进行目标物件的生成( Thread - Per - Message),真正的目标物件生成之后,将之设定至 Future之中,而当客户端真正需要目标物件时,目标物件也已经准备好,可以让客户提取使用。
结合 JDK的 Future来看,就是你 run线程后,你可以把线程的返回值赋给 Future并返回一个 Future对象。这时你可以立即拿到这个对象,然后进行下面的逻辑。但是如果你要 get这个 Future中的线程结果,就会被阻塞直到线程结束。 就相当于购物一样,你下单支付成功之后,一直等待物流,只有等商品送到你手上你才可以进行使用。其他时间你都得等待它。

Runnable 使用

package cn.heh;

import java.text.NumberFormat;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class RunnableDemo implements Runnable {
    //总的车票数
    private int total;

    RunnableDemo(int total) {
        this.total = total;
    }

    //初始车票号码 1
    private int num = 1;

    @Override
    public void run() {
        while (true) {

            synchronized (this) {
                try {
                    // 任何线程获取"线程锁"以后都要先判断是否还有余票,防止等待的线程多打印车票
                    if (num > total) return;
                    // 获取当前线程名字
                    String threadName = Thread.currentThread().getName();
                    // 格式化票号
                    String ticketNum = FormatTicketNum(num++);

                    // 打印火车票,休眠10毫秒模拟打印车票时间
                    Thread.sleep(10);
                    System.out.println(threadName + " 售出火车票No." + ticketNum);
                    // 某线程售完最后一张车票时,放出"车票已售罄"提示
                    if (num > total) {
                        System.out.println("车票已售罄!");
                        return;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 格式化车票号码
     */
    static String FormatTicketNum(int num) {

        NumberFormat nf = NumberFormat.getIntegerInstance();
        nf.setMinimumIntegerDigits(3);
        return nf.format(num);
    }

    public static void main(String[] args) {
        System.out.println("直接调用run()相当于调用本地方法一样并不会启动线程");
        RunnableDemo launch = new RunnableDemo(10);
        launch.run();
        System.out.println("************************************");

        System.out.println("在新线程中启动任务");
        RunnableDemo launch1 = new RunnableDemo(10);
        Thread thread = new Thread(launch1, "小何");
        thread.start();
        System.out.println("************************************");

        RunnableDemo launch2 = new RunnableDemo(500);
        for (int i = 0; i < 5; i++) {
            new Thread(launch2, "售票员" + i).start();
        }

        System.out.println("************************************");
        System.out.println("线程池方式单线程允许");
        RunnableDemo launch3 = new RunnableDemo(500);
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(launch3);


        System.out.println("************************************");
        System.out.println("线程池方式多线程允许");
        RunnableDemo launch4 = new RunnableDemo(500);
        ExecutorService executorService1 = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService1.execute(launch4);
        }
    }

}

运行结果就不在这里展示了。至于为什么我会选择后期使用线程池方式执行。其实使用execute执行效果与上面thread.start一样,都不会有结果返回。再看一下下面的代码

Callable使用实例

package cn.heh;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class CallableDemo implements Callable<String> {
    //总的车票数
    private int total;

    CallableDemo(int total) {
        this.total = total;
    }

    //初始车票号码 1
    private int num = 1;

    @Override
    public String call() throws Exception {
        while (true) {
            synchronized (this) {
                try {
                    // 任何线程获取"线程锁"以后都要先判断是否还有余票,防止等待的线程多打印车票
                    if (num > total) return "车票已售罄!";
                    // 获取当前线程名字
                    String threadName = Thread.currentThread().getName();
                    // 格式化票号
                    String ticketNum = FormatTicketNum(num++);
                    // 打印火车票,休眠10毫秒模拟打印车票时间
                    Thread.sleep(10);
//                    System.out.println(threadName + " 售出火车票No." + ticketNum);
                    return threadName + " 售出火车票No." + ticketNum;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 格式化车票号码
     */
    static String FormatTicketNum(int num) {
        NumberFormat nf = NumberFormat.getIntegerInstance();
        nf.setMinimumIntegerDigits(3);
        return nf.format(num);
    }

    public static void main(String[] args) {


        ExecutorService exec = Executors.newFixedThreadPool(10);
        List<Future<Integer>> results = new ArrayList<>();
        Callable callable = new CallableDemo(100);
        for (int i = 0; i < 10; i++) {
            results.add(exec.submit(callable));
        }
        boolean isDone = false;
        while (!isDone) {
            isDone = true;
            for (Future<Integer> future : results) {
                if (!future.isDone()) {
                    isDone = false;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                    break;
                } else {
                    try {
                        System.out.println(future.get());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        exec.shutdown();
    }
}

submit()方法回产生Future对象,它用Callable返回结果的特定类型进行了参数化。可以用isDone()方法来查询Future是否已经完成,当任务完成时,它具有一个结果,可以调用get()方法获取该结果。也可以不用isDone()进行检查就直接调用get(),在这种情况下,get()将阻塞,直至结果准备就绪。还可以在试图调用get()来获取结果之前,先调用具有超时的get(),或者调用isDone()来查看任务是否完成。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值