Java面试题:创建线程的四种方式

在Java编程语言中,线程是构建并发程序的基本单位。掌握创建线程的四种主要方式是每个Java开发者的基本功之一。本文将详细介绍这四种方式,并提供相关代码示例和状态图、甘特图来帮助你理解它们的工作机制。

1. 继承Thread类

方法简介

Java中可以通过继承Thread类来创建线程。通过重写run()方法,线程将在其执行时执行这个方法中的代码。

代码示例

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - " + i);
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.start();
        t2.start();
    }
}

在这个示例中,两个线程被创建并执行,每个线程都会打印出其名字和一个计数。

2. 实现Runnable接口

方法简介

另一种创建线程的方法是实现Runnable接口。通过实现run()方法,然后将Runnable实例传递给Thread构造函数来启动线程。

代码示例

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - " + i);
        }
    }
}

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

在这个示例中,我们创建了一个实现了Runnable接口的类MyRunnable,然后用它来启动两个线程。

3. 使用Callable和Future

方法简介

Callable接口与Runnable类似,但它可以返回结果,并且可以抛出异常。使用ExecutorService可以创建线程并管理其执行。

代码示例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() {
        int sum = 0;
        for (int i = 0; i < 5; i++) {
            sum += i;
        }
        return sum;
    }
}

public class CallableExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        Future<Integer> future = executor.submit(new MyCallable());
        
        try {
            Integer result = future.get();
            System.out.println("Result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}

在这个示例中,使用Callable来计算一系列整数的和。通过Future来获取结果。

4. 使用Java 8的Lambda表达式

方法简介

如果你使用的是Java 8或更高版本,可以利用Lambda表达式简化线程的创建,特别是实现Runnable接口时。

代码示例

public class LambdaExample {
    public static void main(String[] args) {
        Runnable runnable = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + " - " + i);
            }
        };

        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        thread1.start();
        thread2.start();
    }
}

在这个示例中,我们用Lambda表达式来简化Runnable的实现,代码更加简洁明了。

线程状态图

线程在运行时会经历多种状态。下面是一个线程状态的状态图,展示了不同状态之间的转换。

stateDiagram
    [*] --> 新建
    新建 --> 就绪
    就绪 --> 运行
    运行 --> 阻塞
    运行 --> 死亡
    阻塞 --> 就绪
    运行 --> 就绪
    运行 --> 等待
    等待 --> 就绪

线程执行时间甘特图

以下是对四种创建线程方式在执行时的甘特图表示,展示了各个线程的执行时间。

gantt
    title 线程执行时间
    dateFormat  YYYY-MM-DD    
    section 继承Thread
    Thread1    :a1, 2023-10-01, 1d
    Thread2    :after a1  , 1d
    
    section 实现Runnable
    Runnable1  :a2, 2023-10-01, 1d
    Runnable2  :after a2  , 1d

    section 使用Callable
    Callable    :a3, 2023-10-01, 1d

    section 使用Lambda
    Lambda1     :a4, 2023-10-01, 1d
    Lambda2     :after a4, 1d

总结

在Java中,创建线程有四种主要方式:继承Thread类、实现Runnable接口、使用Callable接口和Java 8的Lambda表达式。每种方式都有其特点和适用场景。

熟练掌握这些线程创建方式,不仅能帮助你在面试中脱颖而出,还能让你在实际开发中更灵活地处理多线程问题。了解线程的状态转换和线程的执行时间对于调试和优化并发程序也至关重要。希望本文能帮助你加深对Java线程创建的理解,助你在Java开发之路上更进一步。