经典面试题:java线程的创建方式

经典面试题:java线程的创建方式

码农世界 2024-05-22 前端 55 次浏览 0个评论

目录

    • 前言
    • 1. 继承Thread类
    • 2. 实现Runnable接口
    • 3.实现Callable接口
      • 3.1使用FutureTask包装Callable
      • 3.2创建并启动线程
      • 4.使用Executor框架
      • 5.应用场景
      • 6总结

        前言

        java线程的创建方式是一道经典的面试题,在Java中,创建线程主要有四种方式,每种方式各有特点,适合不同的使用场景。面试中,不仅要了解其创建方式,更要熟悉其应用场景。

        1. 继承Thread类

        使用:创建一个新的类继承自java.lang.Thread类,并重写其run()方法,在run()方法中定义线程需要执行的逻辑。然后创建这个子类的实例,并调用其start()方法来启动线程。

        优点:实现简单,可以直接操作线程对象。

        缺点:Java不支持多重继承,如果类已经继承了其他类,就不能再继承Thread类。

        示例代码:

        class MyThread extends Thread {
            public void run() {
                System.out.println("通过继承Thread类创建线程");
            }
        }
        public class ThreadExample {
            public static void main(String[] args) {
                MyThread t = new MyThread();
                t.start(); // 启动线程
            }
        }
        

        2. 实现Runnable接口

        使用:定义一个类实现java.lang.Runnable接口,实现run()方法,然后将该类的实例作为参数传递给Thread类的构造函数,创建Thread对象。最后调用Thread对象的start()方法来启动线程。

        优点:由于Java支持接口的多重实现,因此这种方式更为灵活,可以避免单继承的局限性,同时使得线程任务(Runnable对象)与线程(Thread对象)分离,有利于共享资源。

        示例代码:

        class MyRunnable implements Runnable {
            public void run() {
                System.out.println("通过实现Runnable接口创建线程");
            }
        }
        public class ThreadExample {
            public static void main(String[] args) {
                MyRunnable r = new MyRunnable();
                Thread t = new Thread(r);
                t.start(); // 启动线程
            }
        }
        

        3.实现Callable接口

        Callable有些麻烦。

        首先,你需要定义一个类实现Callable接口。Callable接口有一个泛型参数V,表示call()方法的返回类型。call()方法可以抛出异常,这是它与Runnable接口的run()方法的主要区别。

        import java.util.concurrent.Callable;
        public class MyCallable implements Callable {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 0; i <= 100; i++) {
                    sum += i;
                }
                return sum; // 返回求和结果
            }
        }
        

        3.1使用FutureTask包装Callable

        接下来,由于Thread类只能接受Runnable对象,所以需要使用FutureTask来包装Callable对象。FutureTask是一个实现了Runnable接口的类,同时也允许你获取Callable的返回值和检查任务的状态。

        import java.util.concurrent.FutureTask;
        FutureTask futureTask = new FutureTask<>(new MyCallable());
        

        3.2创建并启动线程

        最后,你可以通过创建Thread实例并传入FutureTask对象来启动线程。

        public class Main {
            public static void main(String[] args) {
                FutureTask futureTask = new FutureTask<>(new MyCallable());
                
                Thread thread = new Thread(futureTask);
                thread.start(); // 启动线程
                
                try {
                    // 获取并打印线程执行结果
                    Integer result = futureTask.get();
                    System.out.println("计算结果为:" + result);
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
        

        4.使用Executor框架

        除了上述三种基本方式,Java还提供了Executor框架(位于java.util.concurrent包中),它是创建和管理线程的高级工具。最常用的是ExecutorService接口,它提供了线程池管理功能,通过Executors类的工厂方法可以创建不同类型的线程池,如newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool等。

        优点:线程池可以重用线程,减少线程创建和销毁的开销,提高响应速度和系统的资源利用率。

        示例代码:

        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        class Task implements Runnable {
            public void run() {
                System.out.println("通过Executor框架创建线程");
            }
        }
        public class ThreadPoolExample {
            public static void main(String[] args) {
                ExecutorService executor = Executors.newSingleThreadExecutor();
                executor.execute(new Task()); // 提交任务给线程池执行
                executor.shutdown(); // 关闭线程池
            }
        }
        

        5.应用场景

        在实际开发中,使用ExecutorService和Callable/Runnable结合的方式更为常见,尤其是当需要处理并发任务、管理线程池、获取任务结果或处理异常时。

        后台任务和异步处理场景:当需要执行一些耗时操作但不希望阻塞主线程时,如文件上传、数据库操作、网络请求等,推荐使用ExecutorService搭配Runnable或Callable。对于需要获取结果的场景,应使用Callable。

        定时任务和调度场景:如定时数据同步、定时清理任务等,虽然直接使用 Scheduled ExecutorService(ExecutorService的扩展)更合适,但底层仍然是基于Runnable或Callable的

        批量数据处理场景:处理大量数据,如数据导入导出、数据分析等,通过ExecutorService提交多个任务,根据数据分片或逻辑单元进行并行处理,能显著提升处理速度。

        6总结

        创建线程的方式有四种,要根据不同的需求,选择不同的方式额。

转载请注明来自码农世界,本文标题:《经典面试题:java线程的创建方式》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,55人围观)参与讨论

还没有评论,来说两句吧...

Top