串行化执行、并行化执行
文章目录
- 1、串行化执行
- 2、并行化测试(多线程环境)
- 3、任务的执行是异步的,但主程序的继续执行是同步的
可以将多个任务编排为并行和串行化执行。
也可以处理编排的多个任务的异常,也可以返回兜底数据。
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任务