Bootstrap

串行化执行、并行化执行


可以将多个任务编排为并行和串行化执行。
也可以处理编排的多个任务的异常,也可以返回兜底数据。

1、串行化执行

顺序执行、同步执行
按顺序同步执行
导入 StopWatch 类,这是 Spring 框架提供的一个工具类,用于测量任务的执行时间。

package com.atguigu.structure;

import org.springframework.util.StopWatch;

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        StopWatch stopWatch = new StopWatch();

        // 为每个任务分别计时
        stopWatch.start("a任务");
        a();
        stopWatch.stop();

        stopWatch.start("b任务");
        b();
        stopWatch.stop();

        stopWatch.start("c任务");
        c();
        stopWatch.stop();

        // 此时,StopWatch 已经自动停止了总任务的计时(因为最后一个任务也已经停止)
        // 但如果您想显式地停止总任务(尽管在这个例子中它是多余的),您需要确保没有正在运行的任务

        // 打印结果
        System.out.println(stopWatch.prettyPrint());
    }

    public static void a() throws InterruptedException {
        Thread.sleep(3000);
        System.out.println(Thread.currentThread().getName() + " a任务执行完毕-" + System.currentTimeMillis());
    }

    public static void b() throws InterruptedException {
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName() + " b任务执行完毕-" + System.currentTimeMillis());
    }

    public static void c() throws InterruptedException {
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName() + " c任务执行完毕-" + System.currentTimeMillis());
    }
}

由于 a 任务最先执行,它的开始时间是 0 毫秒,结束时间是 3000 毫秒。b 任务在 a 任务结束后立即开始,因此它的开始时间是 3000 毫秒,结束时间是 5000 毫秒。c 任务在 b 任务结束后立即开始,因此它的开始时间是 5000 毫秒,结束时间是 6000 毫秒。

main a任务执行完毕-1727613404687
main b任务执行完毕-1727613406705
main c任务执行完毕-1727613407711
StopWatch '': 6.0325798 seconds
----------------------------------------
Seconds       %       Task name
----------------------------------------
3.0193421     50%     a任务
2.007708      33%     b任务
1.0055297     17%     c任务

2、并行化测试(多线程环境)

多个并发任务
三个任务将并行执行

注意:这里主线程不会等待任务线程完成,因此程序可能会立即退出。
如果需要等待所有任务完成,可以使用CountDownLatch或其他同步机制。

package com.atguigu.structure;

import org.springframework.util.StopWatch;

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        StopWatch stopWatch = new StopWatch();

        // 为每个任务分别计时
        stopWatch.start("a任务");
        new Thread(()->{
            a();
        }).start();
        stopWatch.stop();

        stopWatch.start("b任务");
        new Thread(()->{
            b();
        }).start();
        stopWatch.stop();

        stopWatch.start("c任务");
        new Thread(()->{
            c();
        }).start();
        stopWatch.stop();

        // 打印结果
        System.out.println(stopWatch.prettyPrint());
    }

    public static void a(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + " a任务执行完毕-" + System.currentTimeMillis());
    }

    public static void b(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + " b任务执行完毕-" + System.currentTimeMillis());
    }

    public static void c(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + " c任务执行完毕-" + System.currentTimeMillis());
    }
}
StopWatch '': 0.0022191 seconds
----------------------------------------
Seconds       %       Task name
----------------------------------------
0.0011501     52%     a任务
0.0005202     23%     b任务
0.0005488     25%     c任务

Thread-2 c任务执行完毕-1727615633886
Thread-1 b任务执行完毕-1727615634883
Thread-0 a任务执行完毕-1727615635882

StopWatch 的 start() 和 stop() 方法调用是在主线程中顺序执行的,但是实际的任务(a(), b(), c())是在不同的线程中异步执行的。这会导致 StopWatch 的 stop() 方法在相应的任务线程实际完成之前就被调用了,因此 StopWatch 记录的时间将远小于任务实际执行的时间。

3、任务的执行是异步的,但主程序的继续执行是同步的

使用f1.get(), f2.get(), f3.get()确保主线程等待这些任务完成。

  • 即每个任务都使用了独立的 FutureTask 和线程实例。但是,有一点需要注意:在 main 线程中调用 f1.get(), f2.get(), 和 f3.get() 会阻塞 main 线程,直到相应的 FutureTask 完成。这实际上意味着 main 线程会等待每个任务完成后再继续执行下一个任务,这可能会使得并行执行的优势变得不那么明显,因为任务实际上是顺序执行的(尽管它们在不同的线程中运行)。
  • 因此,从主线程的角度来看,这些任务并不是“真正”的异步执行,因为主线程在等待每个任务完成。然而,从操作系统或 JVM 的角度来看,这些任务确实是在不同的线程中并行执行的(如果系统资源允许的话)。
  • 从全局角度来看,每个任务虽然是异步启动的,但它们依次等待完成,这使得整个程序看起来是按顺序执行的。
  • 主程序通过调用 f1.get(), f2.get(), 和 f3.get() 等待每个任务完成。这意味着主程序会阻塞,直到所有任务都执行完毕。
  • 这种方式确保了任务的执行时间能够被准确地测量,但也意味着主程序不会在所有任务完成之前继续执行其他操作。
  • 由于您还希望使用 StopWatch 来测量每个任务的执行时间,您需要在某个地方等待这些任务完成,所以通过调用 f1.get(), f2.get(), 和 f3.get() 在主线程中阻塞了这些 FutureTask 的执行结果。

总结来说,任务的执行是异步的,但主程序的继续执行是同步的,因为它等待所有异步任务完成。

package com.atguigu.structure;

import org.springframework.util.StopWatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Demo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        StopWatch stopWatch = new StopWatch();

        // 为每个任务分别计时
        stopWatch.start("a任务");
        FutureTask f1 = new FutureTask<>(() -> {
            a();
            return null;
        });
        new Thread(f1).start();
        f1.get();
        stopWatch.stop();

        stopWatch.start("b任务");
        FutureTask f2 = new FutureTask<>(() -> {
            b();
            return null;
        });
        new Thread(f2).start();
        f2.get();
        stopWatch.stop();

        stopWatch.start("c任务");
        FutureTask f3 = new FutureTask<>(() -> {
            c();
            return null;
        });
        new Thread(f3).start();
        f3.get();
        stopWatch.stop();

        // 打印结果
        System.out.println(stopWatch.prettyPrint());
    }

    public static void a(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + " a任务执行完毕-" + System.currentTimeMillis());
    }

    public static void b(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + " b任务执行完毕-" + System.currentTimeMillis());
    }

    public static void c(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + " c任务执行完毕-" + System.currentTimeMillis());
    }
}
Thread-0 a任务执行完毕-1727619923910
Thread-1 b任务执行完毕-1727619925934
Thread-2 c任务执行完毕-1727619926943
StopWatch '': 6.0475203 seconds
----------------------------------------
Seconds       %       Task name
----------------------------------------
3.0258235     50%     a任务
2.0129727     33%     b任务
1.0087241     17%     c任务

在这里插入图片描述

;