面试官: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  进粉丝群或围观朋友圈



浏览 74
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐