JAVA多线程实现的四种方式及使用场景详解
作者:日暮温柔
一、继承Thread类实现多线程
- 步骤一:定义线程类
创建一个类继承自Thread类,并重写run方法。run方法中的代码就是线程执行的内容。
例如:
class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("MyThread: " + i); } } }
- 步骤二:启动线程
在main方法或者其他合适的地方创建线程对象,然后调用start方法来启动线程。
例如:
public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); for (int i = 0; i < 10; i++) { System.out.println("Main Thread: " + i); } } }
注意,不能直接调用run方法来启动线程。如果直接调用run方法,就相当于在当前线程中执行run方法中的代码,而不是开启一个新的线程。
二、实现Runnable接口实现多线程
- 步骤一:定义任务类
创建一个类实现Runnable接口,实现run方法。这个run方法包含了线程要执行的任务。
例如:
java class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("MyRunnable: " + i); } } }
- 步骤二:创建线程并启动
首先创建Runnable对象,然后将其作为参数传递给Thread对象的构造函数,最后调用Thread对象的start方法来启动线程。
例如:
public class Main { public static void main(String[] args) { MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); for (int i = 0; i < 10; i++) { System.out.println("Main Thread: " + i); } } }
实现Runnable接口的优势
这种方式更灵活,因为一个类可以实现多个接口,避免了单继承的限制。例如,如果一个类已经继承了其他类,还想实现多线程功能,就可以使用实现Runnable接口的方式。而且Runnable对象可以被多个线程共享,方便在多个线程中执行相同的任务。
三、使用Callable和Future实现多线程(带有返回值)
- 步骤一:定义Callable任务类
创建一个类实现Callable接口,实现call方法。call方法中包含线程要执行的任务,并且可以有返回值。
例如:
import java.util.concurrent.Callable; class MyCallable implements Callable<Integer> { @Override public Integer call() { int sum = 0; for (int i = 0; i < 10; i++) { sum += i; } return sum; } }
- 步骤二:提交任务并获取结果
通过ExecutorService来提交Callable任务。ExecutorService可以通过Executors工厂类来创建。
例如:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newSingleThreadExecutor(); MyCallable callable = new MyCallable(); Future<Integer> future = executorService.submit(callable); System.out.println("计算结果: " + future.get()); executorService.shutdown(); } }
在这里,submit方法提交Callable任务并返回一个Future对象,通过Future对象的get方法可以获取Callable任务的返回值。shutdown方法用于关闭ExecutorService。
四、线程池的使用(ExecutorService)
- 步骤一:创建线程池
可以使用Executors工厂类创建不同类型的线程池,如newFixedThreadPool(固定大小线程池)、newCachedThreadPool(可缓存线程池)、newSingleThreadExecutor(单线程线程池)等。
例如,创建一个固定大小为 5 的线程池:
ExecutorService executorService = Executors.newFixedThreadPool(5);
- 步骤二:提交任务到线程池
可以使用execute方法提交Runnable任务,或者使用submit方法提交Callable任务到线程池。
例如,提交一个Runnable任务:
class MyRunnableInPool implements Runnable { @Override public void run() { System.out.println("线程池中的线程在执行任务"); } } public class Main { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { executorService.execute(new MyRunnableInPool()); } executorService.shutdown(); } }
- 步骤三:关闭线程池
当任务执行完毕后,需要关闭线程池。可以使用shutdown方法来正常关闭线程池,它会等待所有已提交的任务执行完毕后再关闭。如果想要立即关闭线程池,可以使用shutdownNow方法,但这种方式可能会导致正在执行的任务被中断。
例如:
executorService.shutdown();
Q:一个管理系统的java项目,什么时候会需要用到多线程呢?
1、数据导入与导出功能
背景和需求:
在管理系统中,经常需要进行数据的导入和导出操作。例如,将大量的用户信息、订单数据等从外部文件(如 CSV、Excel 文件)导入到数据库中,或者将系统中的数据导出为报表文件。这些操作可能涉及大量的数据处理,如果在单线程中执行,会导致界面长时间无响应,用户体验差。
多线程的应用方式:
可以开启一个单独的线程来执行数据导入或导出任务。这样,在数据处理的同时,用户界面仍然可以响应用户的其他操作,如查看其他数据、进行系统设置等。例如,当用户点击 “导入数据” 按钮时,系统在后台线程中读取文件、解析数据并插入到数据库,而前台线程继续响应用户的其他交互。
2、数据缓存更新与维护
背景和需求:
为了提高系统性能,管理系统通常会使用数据缓存。缓存的数据需要定期更新,以保证数据的一致性和时效性。例如,缓存的商品库存信息、用户权限信息等需要根据数据库中的最新数据进行更新。
多线程的应用方式:
可以使用一个线程定期(如每隔一段时间)检查缓存数据是否过期,并在需要时更新缓存。这个线程可以在后台默默地运行,不影响系统的其他主要功能。同时,在数据发生变化(如用户修改了商品库存)时,也可以通过多线程机制及时更新缓存,以减少对系统其他操作的影响。
3、并发用户操作处理
背景和需求:
在多用户使用的管理系统中,会有多个用户同时进行各种操作,如查询数据、修改记录、提交表单等。如果系统是单线程的,这些操作只能依次进行,效率低下。
多线程的应用方式:
为每个用户请求分配一个独立的线程来处理。例如,在一个在线商城管理系统中,当多个管理员同时修改商品价格、处理订单时,系统可以为每个管理员的操作开启一个线程,这些线程可以并发地访问和修改数据库中的数据(当然,需要注意数据库连接池的合理使用和数据的一致性问题),从而提高系统的并发处理能力。
4、系统监控与日志记录
背景和需求:
管理系统需要对自身的运行状态进行监控,如系统资源使用情况(CPU、内存、磁盘 I/O 等)、服务的可用性等。同时,需要记录用户操作日志、系统错误日志等信息。
多线程的应用方式:
可以使用一个线程专门负责系统监控,定期收集系统状态数据并进行分析。另一个线程可以负责将日志信息写入日志文件,这样可以避免日志记录操作阻塞其他业务操作,并且在系统出现问题时能够及时记录相关信息,方便后续的故障排查。
5、定时任务执行
背景和需求:
管理系统中有很多定时任务,如每天定时生成销售报表、每月定时结算员工工资等。这些任务需要在特定的时间自动执行,并且不能影响系统的正常运行。
多线程的应用方式:
通过线程池来管理定时任务线程。例如,使用 Java 中的ScheduledExecutorService来安排定时任务。系统可以根据任务的执行时间和频率,在后台线程中自动执行这些任务,而不会干扰系统的日常操作。
Q:只要继承了runnable接口,用这个类的时候就开启多线程了吗?
继承Runnable接口只是定义了任务内容
当一个类实现了Runnable接口,它仅仅是定义了一个线程要执行的任务逻辑,这个任务逻辑包含在run方法中。
例如:
class MyRunnable implements Runnable { @Override public void run() { System.out.println("执行自定义任务"); } }
上述代码定义了一个名为MyRunnable的类,它实现了Runnable接口,run方法里是具体要执行的任务。但此时,只是定义了任务,还没有开启多线程。
需要通过Thread类来启动线程执行任务
要真正开启一个新的线程来执行这个任务,需要将Runnable对象作为参数传递给Thread类的构造函数,然后调用Thread对象的start方法。例如:
java public class Main { public static void main(String[] args) { MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); } }
在这个例子中,首先创建了MyRunnable类的一个对象runnable,然后通过new Thread(runnable)创建了一个Thread对象,这个Thread对象将执行runnable对象中定义的任务。最后,调用thread.start()方法来启动线程。如果只是创建了
Thread对象而没有调用start方法,任务也不会在新的线程中执行,和普通方法调用没有区别。
所以,仅仅继承Runnable接口并没有开启多线程,它只是提供了一种方便的方式来定义线程要执行的任务,还需要通过Thread类来实际启动线程,让任务在新的线程环境中运行。
总结
到此这篇关于JAVA多线程实现的四种方式及使用场景的文章就介绍到这了,更多相关JAVA多线程实现方式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!