目录
- 前言
- 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总结
创建线程的方式有四种,要根据不同的需求,选择不同的方式额。
还没有评论,来说两句吧...