JAVA面试——创建线程有几种方法?

1.继承Thread类:创建一个类,继承自 Thread 类,并重写 run() 方法来定义线程的执行逻辑。然后可以实例化这个类并调用 start() 方法来启动线程。

public class MyThread extends Thread {
    @Override
    public void run() {
        // 线程执行逻辑
        for (int i = 0; i < 5; i++) {
            System.out.println("当前线程: " + Thread.currentThread().getName() + ",i = " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

在这个例子中,MyThread 类继承了 Thread 类,并重写了 run() 方法来定义线程的执行逻辑,这里简单地打印输出一些信息并暂停 1 秒。在 main 方法中,我们实例化了 MyThread 类并调用 start() 方法来启动线程。

继承 Thread 类的方式相对简单直接,但也存在一些局限性,因为 Java 不支持多重继承,所以继承了 Thread 类之后就无法再继承其他类。另外,如果想要共享实例变量,也不太方便。

总结起来,继承 Thread 类是一种简单直接的创建线程的方式,适合一些简单的线程逻辑,但在复杂的场景中可能需要考虑其他方式。

2.实现Runnable接口:创建一个类,实现 Runnable 接口,并实现 run() 方法来定义线程的执行逻辑。然后可以将这个类的实例传递给 Thread 类的构造函数来创建线程。

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程执行逻辑
        for (int i = 0; i < 5; i++) {
            System.out.println("当前线程: " + Thread.currentThread().getName() + ",i = " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

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

在这个例子中,MyRunnable 类实现了 Runnable 接口,并重写了 run() 方法来定义线程的执行逻辑,这里简单地打印输出一些信息并暂停 1 秒。在 main 方法中,我们创建了 MyRunnable 的实例,并将其传递给 Thread 的构造函数来创建线程。最后调用 thread.start() 来启动线程。

需要注意的是,run() 方法不会返回任何结果,没有返回值。如果需要获取线程的执行结果,可以使用 Callable 接口来代替。

总结起来,实现 Runnable 接口是一种比较常见的创建线程的方式,它不仅简单易用,而且可以方便地共享实例变量。

3.实现Callable接口:它可以让线程执行完后返回一个结果或者抛出一个异常。与 Runnable 接口不同的是,Callable 接口中的 call() 方法可以声明抛出异常,并且可以有返回值。

import java.util.concurrent.Callable;

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

public class Main {
    public static void main(String[] args) throws Exception {
        Callable myCallable = new MyCallable();
        FutureTask futureTask = new FutureTask<>(myCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println("计算结果:" + futureTask.get());
    }
}

在这个例子中,MyCallable 类实现了 Callable 接口,并实现了 call() 方法来定义线程的执行逻辑,这里是计算 1-100 的和并返回。在 main 方法中,我们将 MyCallable 实例传递给 FutureTask 的构造函数,再将 FutureTask 实例传递给 Thread 的构造函数来创建线程并启动执行。最后调用 futureTask.get() 方法来获取线程的返回结果。

需要注意的是,call() 方法可以声明抛出异常,并且可以有返回值。同时,在本例中,我们使用了 FutureTask 来获取线程执行结果,它是一个包装器,可以将 Callable 转换成 Future。

总之,实现 Callable 接口可以让我们更方便地同时获取线程的返回结果和异常信息,提高了代码的可靠性和可读性。

4.使用线程池的方式创建线程:使用线程池可以更好地管理和复用线程资源,从而提高程序的性能和效率。Java 提供了 Executor 框架来创建和管理线程池。

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

public class Main {
    public static void main(String[] args) {
        // 创建线程池,指定线程数为 5
        ExecutorService executor = Executors.newFixedThreadPool(5);

        // 提交任务给线程池
        for (int i = 0; i < 10; i++) {
            Runnable worker = new WorkerThread("任务 " + (i + 1));
            executor.execute(worker);
        }

        // 关闭线程池
        executor.shutdown();
    }
}

class WorkerThread implements Runnable {
    private String taskName;

    public WorkerThread(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 开始执行任务:" + taskName);
        try {
            // 模拟任务执行时间
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 完成任务:" + taskName);
    }
}

在这个例子中,我们通过 Executors.newFixedThreadPool(5) 创建了一个固定大小为 5 的线程池。然后我们提交了 10 个任务给线程池,每个任务都是一个实现了 Runnable 接口的 WorkerThread 类。每个任务会被线程池中的一个线程执行。

最后,我们调用 executor.shutdown() 关闭线程池。这会等待所有已提交的任务执行完毕,并且不再接受新的任务。

使用线程池可以有效地管理线程的创建和销毁,避免了频繁创建和销毁线程的开销。此外,线程池还提供了一些额外的功能,例如线程调度、线程超时等,能更好地控制线程的执行。

总结起来,使用线程池能够更好地管理和复用线程资源,提高程序的性能和效率。

你可能感兴趣的:(java,面试,开发语言)