面试官:Java 中 runnable 和 callable 有什么区别?并用代码具体举例说明

我是程序汪

共 8420字,需浏览 17分钟

 ·

2024-06-18 12:02

Java中的RunnableCallable接口都是为了实现多线程而设计的,但它们之间存在一些关键差异:

主要区别:

  1. 返回值

    • Runnable接口的run()方法没有返回值(返回类型为void),适用于不需要线程返回结果的情景。
    • Callable接口的call()方法可以有返回值(通过泛型指定返回类型),并且该方法可以抛出异常,这使得它更强大,适合需要线程执行后返回结果的场景。
  2. 异常处理

    • Runnablerun()方法不能声明抛出检查异常(checked exceptions),因此处理异常需要在方法内部进行。
    • Callablecall()方法可以声明抛出异常,包括检查异常,使得异常处理更加灵活和规范。

代码示例:

Runnable 示例:

class PrintTask implements Runnable {
    private String message;

    public PrintTask(String message) {
        this.message = message;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ": " + message);
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        Thread thread = new Thread(new PrintTask("Hello from Runnable"));
        thread.start();
    }
}

Callable 示例:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class ComputeTask implements Callable<Integer{
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
        return sum;
    }
}

public class CallableExample {
    public static void main(String[] args) {
        ComputeTask task = new ComputeTask();
        FutureTask<Integer> futureTask = new FutureTask<>(task);
        Thread thread = new Thread(futureTask);
        thread.start();

        try {
            Integer result = futureTask.get(); // 获取Callable线程的返回值
            System.out.println("Sum: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

CallableExample中,我们创建了一个实现了Callable<Integer>接口的ComputeTask类,它计算0到99的和,并通过FutureTask包装后启动线程。主线程通过futureTask.get()方法获取到了Callable线程的执行结果。而在RunnableExample中,PrintTask只是简单打印一条消息,没有返回值。


Java实现多线程有多种方式,下面我会列举几种主要的实现方式并提供相应的代码示例。

1. 继承 Thread

创建一个新的类继承自 Thread 类,并重写 run 方法来定义线程执行的代码。

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承Thread类方式运行");
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start(); // 启动线程
    }
}

2. 实现 Runnable 接口

定义一个类实现 Runnable 接口,并实现 run 方法。然后创建 Thread 对象,将实现了 Runnable 的对象作为参数传递给 Thread 构造器。

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口方式运行");
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
    }
}

3. 实现 Callable 接口(通过 FutureTask 包装)

Callable 接口类似于 Runnable,但是它可以返回结果,并且可以抛出异常。需要通过 FutureTask 封装后,再作为 Thread 的目标。

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<String{
    @Override
    public String call() throws Exception {
        return "Callable方式运行";
    }
}

public class CallableExample {
    public static void main(String[] args) {
        MyCallable myCallable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(myCallable);
        Thread t = new Thread(futureTask);
        t.start();
        
        try {
            String result = futureTask.get(); // 获取返回值
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4. 使用线程池(ExecutorService

通过 ExecutorService 提供的线程池来管理和控制线程,可以有效复用线程,减少资源开销。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class Task implements Runnable {
    @Override
    public void run() {
        System.out.println("通过线程池运行");
    }
}

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2); // 创建固定大小线程池
        executor.submit(new Task());
        executor.shutdown(); // 关闭线程池
        try {
            executor.awaitTermination(5, TimeUnit.SECONDS); // 等待所有任务完成
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

以上是Java中实现多线程的几种主要方式,每种方式都有其适用场景,开发者可以根据实际需求选择最合适的实现方式。



程序汪接私活项目目录,2023年总结

Java项目分享  最新整理全集,找项目不累啦 07版

程序汪10万接的无线共享充电宝项目,开发周期3个月

程序汪1万接的企业官网项目,开发周期15天

程序汪8万接的共享口罩项目,开发周期1个月

程序汪8万块的饮水机物联网私活项目经验分享

程序汪接的4万智慧餐饮项目

程序汪接的酒店在线开房项目,另外一个好听的名字叫智慧酒店


欢迎添加程序汪个人微信 itwang008  进粉丝群或围观朋友圈



浏览 49
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报