这里只是一些简单基础的使用,自己浅薄的见解
1、使用 join()
可以在一个线程中调用另一个线程的join()方法,使得当前线程等待被调用线程执行完毕后再继续执行。
通过在 T1 中调用 T2 的 join() 方法,确保 T2 执行完毕后才继续执行 T1,然后在 T2 中调用 T3 的 join() 方法,确保 T3 执行完毕后才继续执行 T2。
Thread t1 = new Thread(() -> {
// T1的任务逻辑
System.out.println("Thread T1 execute ");
});
Thread t2 = new Thread(() -> {
try {
t1.join(); // T2等待T1执行完成
} catch (InterruptedException e) {
e.printStackTrace();
}
// T2的任务逻辑
System.out.println("Thread T2 execute ");
});
Thread t3 = new Thread(() -> {
try {
t2.join(); // T3等待T2执行完成
} catch (InterruptedException e) {
e.printStackTrace();
}
// T3的任务逻辑
System.out.println("Thread T3 execute ");
});
t1.start();
t2.start();
t3.start();
注意的是,使用join()方法会阻塞当前线程,直到被等待的线程执行完成。
2、使用 Lock 和 Condition
可以使用 Lock 和 Condition 来实现线程之间的顺序执行。
在 T1 中调用 condition1.await() 方法等待条件满足,然后在 T2 中调用 condition1.signal() 方法唤醒 T1 继续执行,再在 T2 中调用 condition2.await() 方法等待条件满足,最后在 T3 中调用 condition2.signal() 方法唤醒 T2 继续执行。
public static void main(String[] args) {
ThreeThreadExecuteSequentially example = new ThreeThreadExecuteSequentially();
new Thread(example::thread1, "A").start();
new Thread(example::thread2, "B").start();
new Thread(example::thread3, "C").start();
}
// 线程执行的条件 1:线程1执行 2:线程2执行 3:线程3执行
int number = 1;
// 锁
Lock lock = new ReentrantLock();
// 从锁中获得3个条件变量
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
// 第一个线程run之后执行的方法
public void thread1() {
lock.lock();
try {
// 如果条件值不为1 就挂起等待
while (number != 1) {
condition1.await();
}
System.out.println("------1------");
// 线程1 执行完毕 把变量设置为2
number = 2;
// 唤醒第2个条件变量
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 不管抛没抛出异常都要解锁,防止线程死锁
lock.unlock();
}
}
public void thread2() {
lock.lock();
try {
while (number != 2) {
condition2.await();
}
System.out.println("------2------");
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void thread3() {
lock.lock();
try {
while (number != 3) {
condition3.await();
}
System.out.println("------3------");
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
3、使用 CountDownLatch
可以使用 CountDownLatch 来实现线程之间的顺序执行。
在 T1 中调用 countDownLatch1.await() 方法等待计数器减到 0,然后在 T2 中调用 countDownLatch1.countDown() 方法减少计数器,再在 T2 中调用 countDownLatch2.await() 方法等待计数器减到 0,最后在 T3 中调用 countDownLatch2.countDown() 方法减少计数器。
// 创建两个 CountDownLatch 实例
CountDownLatch countDownLatch1 = new CountDownLatch(1);
CountDownLatch countDownLatch2 = new CountDownLatch(1);
// 创建线程 T1
Thread t1 = new Thread(() -> {
try {
// T1 线程等待计数器减到 0
countDownLatch1.await();
System.out.println("T1 is running");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 创建线程 T2
Thread t2 = new Thread(() -> {
try {
// T2 线程减少计数器
countDownLatch1.countDown();
// T2 线程等待计数器减到 0
countDownLatch2.await();
System.out.println("T2 is running");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 创建线程 T3
Thread t3 = new Thread(() -> {
try {
// T3 线程减少计数器
countDownLatch2.countDown();
System.out.println("T3 is running");
} catch (Exception e) {
e.printStackTrace();
}
});
// 启动线程
t1.start();
t2.start();
t3.start();
4、使用CompletableFuture
CompletableFuture是JDK8的新特性。
CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步会点、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。
这里只简单的使用串行执行
// 创建三个CompletableFuture对象
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// 执行T1任务
System.out.println("T1: " + Thread.currentThread().getName());
}).thenRunAsync(() -> {
// 执行T2任务
System.out.println("T2: " + Thread.currentThread().getName());
}).thenRunAsync(() -> {
// 执行T3任务
System.out.println("T3: " + Thread.currentThread().getName());
});
// 等待所有任务完成,并获取结果
future.join();
5、使用AsyncTool
https://gitee.com/jd-platform-opensource/asyncTool
这个工具是京东零售开源项目,是基于CompletableFuture进行封装的框架,弥补了CompletableFuture的不足。
- worker: 一个最小的任务执行单元。通常是一个网络调用,或一段耗时操作。
- callBack:对每个worker的回调。worker执行完毕后,会回调该接口,带着执行成功、失败、原始入参、和详细的结果。
- wrapper:组合了worker和callback,是一个 最小的调度单元 。通过编排wrapper之间的关系,达到组合各个worker顺序的目的。
先创建三个worker
class TaskWorkerA implements IWorker<String, String>, ICallback<String, String> {
/**
* 在这里做耗时操作,如rpc请求、IO等
*/
@Override
public String action(String s, Map<String, WorkerWrapper> map) {
System.out.println("TaskWorker A run ....");
return "TaskWorker A";
}
@Override
public void result(boolean b, String s, WorkResult<String> workResult) {
}
}
.
.
.
.
然后开始串行任务
TaskWorkerA taskWorkerA = new TaskWorkerA();
TaskWorkerB taskWorkerB = new TaskWorkerB();
TaskWorkerC taskWorkerC = new TaskWorkerC();
WorkerWrapper<String, String> workerWrapper3 = new WorkerWrapper.Builder<String, String>()
.worker(taskWorkerC)
.callback(taskWorkerC)
.param("3")
.build();
WorkerWrapper<String, String> workerWrapper2 = new WorkerWrapper.Builder<String, String>()
.worker(taskWorkerB)
.callback(taskWorkerB)
.param("2")
.next(workerWrapper3)
.build();
WorkerWrapper<String, String> workerWrapper1 = new WorkerWrapper.Builder<String, String>()
.worker(taskWorkerA)
.callback(taskWorkerA)
.param("1")
.next(workerWrapper2)
.build();
try {
Async.beginWork(1500, workerWrapper1);
} catch (Exception e) {
e.printStackTrace();
}
Async.shutDown();