Java中的线程池本身并不提供内置的方式来保证任务的顺序执行的,因为线程池的设计目的是为了提高并发性能和效率,如果顺序执行的话,那就和单线程没区别了。
但是如果被问到想要实现这个功能该怎么做,有以下两种方式。
1.使用单线程的线程池
这就像是一个只有一个员工的公司,员工会按照任务来的顺序,一个接一个的完成任务。在 Java 中,这个员工被称为线程,你可以使用Executors.newSingleThreadExecutor()来创建这样一个线程池。
public class Main {public static void main(String[] args) {//创建一个新的单线程执行器ExecutorService executor = Executors.newSingleThreadExecutor();//创建三个简单的Runnable任务Runnable task1 = () -> System.out.println("1");Runnable task2 = () -> System.out.println("2");Runnable task3 = () -> System.out.println("3");//提交任务到执行器executor.submit(task1);executor.submit(task2);executor.submit(task3);//关闭执行器executor.shutdown();}
}
在这个示例中,我们创建了一个单线程的执行器,并提交了三个任务。每个任务就是一个打印一个数字的简单Runnable对象。因为我们使用的是单线程执行器,所以这些任务会按照它们被提交的顺序执行。输出将会是:
1
2
3
这种方法的优点是你可以确保任务按照提交的顺序执行。然而,它的缺点是你无法利用多线程的优势,因为所有的任务都在一个线程中顺序执行,所以任务的执行速度将被限制为这个线程的速度。
2.使用定时的线程池并设置任务之间的依赖关系
这就像是一个只有一个员工的公司,但是这次,每个任务都需要等待前一个任务完成后才能开始。在 Java 中,你可以使用Executors.newScheduledThreadPool(1)来创建这样一个线程池,并使用ScheduledFuture.get()来设置任务之间的依赖关系。
public class Main {public static void main(String[] args) {try {//创建一个新的定时线程池执行器ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);//创建三个简单的callable任务Callable<Integer> task1 = () -> {System.out.println("1");return 0; };Callable<Integer> task2 = () -> {System.out.println("2");return 0; };Callable<Integer> task3 = () -> {System.out.println("3");return 0; };//提交任务到任务执行器,并获取Futu对象//这段代码表示将任务task1提交给executor,立即执行(延迟时间为0毫秒)。ScheduledFuture<Integer> future1 = executor.schedule(task1, 0, TimeUnit.MILLISECONDS);future1.get();//等待task1完成ScheduledFuture<Integer> future2 = executor.schedule(task2, 0, TimeUnit.MILLISECONDS);future2.get();//等待task2完成ScheduledFuture<Integer> future3 = executor.schedule(task3, 0, TimeUnit.MILLISECONDS);future3.get();//等待task3完成//关闭执行器executor.shutdown();} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}
在这个示例中,我们创建了一个定时线程池执行器,并提交了三个任务。每个任务都是一个打印一个数字并返回0的简单Callable对象。我们使用ScheduledFuture.get()方法来等待前一个任务完成,然后提交下一个任务。这样,我们可以确保任务按照依赖关系顺序执行。
不过要注意,这种方法的缺点是,如果前一个任务没有正常结束(例如抛出了异常),那么所有依赖它的后续任务都无法开始。同时,也需要处理可能的InterruptedException和ExecutionException。
声明:本文基于 Hollis 面经的简单版本解释