赞
踩
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/q258523454/article/details/117998310
假如有3个线程(A,B,C),怎么让它们按照指定的顺序执行任务呢?假设希望按照 A->B->C 的顺序执行。
本文将依次介绍8种方式
目录
4.ReentrantLock-Condition(重入锁)
5.CountDownLatch(减数器)/ CyclicBarrier(栅栏)
8.猜一猜?
下面的代码可以直接复制,运行。
这种方式不推荐,纯粹的是为了实现这功能。
@SneakyThrows
public static void main(String[] args)
{
// 创建三个线程 A,B,C
Thread threadA = new Thread(() -> System.out.println("A"), "A");
Thread threadB = new Thread(() -> System.out.println("B"), "B");
Thread threadC = new Thread(() -> System.out.println("C"), "C");
// 使用 Thread.join() 等待线程执行完毕, 这种方式不优雅
threadA.start();
threadA.join();
threadB.start();
threadB.join();
threadC.start();
threadC.join();
System.exit(0);
}
执行结果:
A
B
C
用单个线程池来实现顺序执行,这种方式比较简单,就是利用单线程池的FIFO原理。
先定义一个Runnable
- @Slf4j
- public class MyRunnable implements Runnable {
-
- /**
- * 自定义线程名
- */
- private String threadName;
-
- /**
- * CountDownLatch指令
- */
- private CountDownLatch latch;
-
- public MyRunnable(String threadName, CountDownLatch latch) {
- this.threadName = threadName;
- this.latch = latch;
- }
-
- @Override
- public void run()
- {
- try {
- // 等待 countDownLatch.countDown() 命名
- latch.await();
- System.out.println(threadName);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
创建单个线程池
- /**
- * 利用单线程池达到顺序执行
- * {@link java.util.concurrent.LinkedBlockingQueue} 保证了FIFO顺序
- */
- public static void main(String[] args) {
- ExecutorService executorService = Executors.newFixedThreadPool(1);
- // CountDownLatch减数机制,countDown触发await,达到同时执行的目的
- CountDownLatch latch = new CountDownLatch(1);
-
- List<String> threadNameList = Arrays.asList("A", "B", "C");
- for (int i = 0; i < 3; i++) {
- executorService.execute(new MyRunnable(threadNameList.get(i), latch));
- }
-
- latch.countDown();
- executorService.shutdown();
- while (!executorService.isTerminated()) {
- // 等待所有线程执行完成
- }
- System.exit(0);
- }
执行结果:A->B->C
不推荐这种方式,原因:不灵活
注意这里有坑,wait()的线程必须要先执行,否则其他线程notify()是无法唤醒的。换句话说,object的这种方式,锁的使用方式一定是先wait()再notify()。
为了简单说明,这里只写A,B两个线程,以A->B顺序执行的示例。
执行代码:
- @Slf4j
- public class Test {
-
- /**
- * 使用 object.wait()/object.notify()
- */
- @SneakyThrows
- public static void main(String[] args) {
- final Object pv = new Object();
-
- ThreadB b = new ThreadB();
- ThreadA a = new ThreadA(b);
-
- // 期望顺序 A->B
- ExecutorService executorService = Executors.newFixedThreadPool(10);
-
- // 这里必须要让B先执行才能正常执行,否则会让B一直处于wait()
- // 原因: 如果A先拿到对象锁,执行notify()无法唤醒B, 因为B还没有拿到对象锁,还没有执行wait()
- // 因此: 同一个锁的执行顺序一定是 wait()-notify()
- executorService.execute(b);
- Thread.sleep(100);
- executorService.execute(a);
-
-
- // 停止接受新任务,当已有任务将执行完,关闭线程池
- executorService.shutdown();
- while (!executorService.isTerminated()) {
- // 等待所有线程执行完成
- }
- System.exit(0);
- }
-
- static class ThreadB implements Runnable {
- @Override
- public void run() {
- synchronized (this) {
- try {
- log.info("B线程等待A线程 doing");
- // wait()的执行前提是当前线程获取了对象控制权,否则会报错:java.lang.IllegalMonitorStateException
- this.wait();
- log.info("B线程等待A线程 done");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("B线程执行完成.");
- }
- }
- }
-
- @AllArgsConstructor
- static class ThreadA implements Runnable {
- private final Object obj;
-
- @SneakyThrows
- @Override
- public void run() {
- synchronized (obj) {
- // notify()不会立马释放对象锁,释放情景: 1.synchronized代码块执行完成; 2.主动释放 wait();
- obj.notify();
- log.info("A线程开始执行.");
- log.info("A线程执行完成.");
- }
- }
- }
- }
执行结果:
B线程等待A线程 doing
A线程开始执行.
A线程执行完成.
B线程等待A线程 done
B线程执行完成.
这种方式比前一种方式灵活些。原理类似。
实现代码:
- @Slf4j
- public class Test {
-
- /**
- * 重入锁实现(ReentrantLock-Condition)
- */
- @SneakyThrows
- public static void main(String[] args) {
-
- ExecutorService executorService = Executors.newFixedThreadPool(3);
-
- final SequenceLock sequenceLock = new SequenceLock();
- Runnable a = () -> sequenceLock.a();
- Runnable b = () -> sequenceLock.b();
- Runnable c = () -> sequenceLock.c();
- executorService.execute(a);
- executorService.execute(b);
- executorService.execute(c);
-
- Thread.sleep(1000);
- sequenceLock.getLock().lock();
- try {
- // 唤醒A线程,依次执行A,B,C
- sequenceLock.getConditionA().signal();
- } catch (Exception ex) {
- //TODO
- } finally {
- sequenceLock.getLock().unlock();
- }
- System.exit(0);
- }
-
- @Data
- static class SequenceLock {
- private ReentrantLock lock = new ReentrantLock();
- private Condition conditionA = lock.newCondition();
- private Condition conditionB = lock.newCondition();
- private Condition conditionC = lock.newCondition();
-
- public void a() {
- lock.lock();
- try {
- log.info("A,wait signal");
- // wait()的执行前提是当前线程获取了对象控制权,否则会报错:java.lang.IllegalMonitorStateException
- conditionA.await();
- log.info("A");
- // 同 notify()一样,并不会立马释放锁,等程序全部执行完,直到 unlock
- conditionB.signal();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
-
- public void b() {
- lock.lock();
- try {
- log.info("B,wait signal");
- conditionB.await();
- log.info("B");
- conditionC.signal();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
-
- public void c() {
- lock.lock();
- try {
- log.info("C,wait signal");
- conditionC.await();
- log.info("C");
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- }
- }
执行结果:
A,wait signal
B,wait signal
C,wait signal
A
B
C
利用CountDownLatch、CyclicBarrier 当成信号变量,作顺序开关。这里只写CountDownLatch代码,CyclicBarrier是类似的。注意:这里需要两个信号通知,因此需要定义两个CountDownLatch。
代码如下:
- @Slf4j
- public class Test {
-
- /**
- * CountDownLatch(减数器)实现/ CyclicBarrier(栅栏)实现
- * {@link CountDownLatch}
- * {@link CyclicBarrier}
- */
- public static void main(String[] args) {
- // 3个线程需要2两个信号通知,A->B需要一个,B->C需要一个
- CountDownLatch signalAB = new CountDownLatch(1);
- CountDownLatch signalBC = new CountDownLatch(1);
-
- A a = new A(signalAB);
- B b = new B(signalAB,signalBC);
- C c = new C(signalBC);
-
- // 线程池中 按顺序执行 A->B->C
- ExecutorService executorService = Executors.newFixedThreadPool(10);
- executorService.execute(b);
- executorService.execute(c);
- executorService.execute(a);
-
- // 停止接受新任务,当已有任务将执行完,关闭线程池
- executorService.shutdown();
- while (!executorService.isTerminated()) {
- // 等待所有线程执行完成
- }
- System.out.println("over");
- System.exit(0);
-
- }
-
- @AllArgsConstructor
- static class A implements Runnable {
- private CountDownLatch latchAB;
-
- @SneakyThrows
- @Override
- public void run() {
- log.info("A");
- latchAB.countDown();
- }
- }
- @AllArgsConstructor
- static class B implements Runnable {
- private CountDownLatch latchAB;
- private CountDownLatch latchBC;
-
- @SneakyThrows
- @Override
- public void run() {
- latchAB.await();
- log.info("B");
- latchBC.countDown();
- }
- }
-
- @AllArgsConstructor
- static class C implements Runnable {
- private CountDownLatch latchBC;
-
- @SneakyThrows
- @Override
- public void run() {
- latchBC.await();
- log.info("C");
- }
- }
- }
执行结果:
A->B->C
其实跟CountDownLatch 一样,都是作为信号量来传递。同样定义两个信号量。执行结果仍然是A->B->C。
代码如下:
- @Slf4j
- public class Test {
-
- /**
- * Semaphore 信号量实现
- * {@link Semaphore}
- */
- public static void main(String[] args) {
- // 3个线程需要2两个信号量,A->B需要一个,B->C需要一个
- Semaphore signalAB = new Semaphore(0);
- Semaphore signalBC = new Semaphore(0);
-
- A a = new A(signalAB);
- B b = new B(signalAB, signalBC);
- C c = new C(signalBC);
-
- // 线程池中 按顺序执行 A->B->C
- ExecutorService executorService = Executors.newFixedThreadPool(10);
- executorService.execute(b);
- executorService.execute(c);
- executorService.execute(a);
-
- // 停止接受新任务,当已有任务将执行完,关闭线程池
- executorService.shutdown();
- while (!executorService.isTerminated()) {
- // 等待所有线程执行完成
- }
- System.out.println("over");
- System.exit(0);
-
- }
-
- @AllArgsConstructor
- static class A implements Runnable {
- private Semaphore semaphoreAB;
-
- @SneakyThrows
- @Override
- public void run() {
- log.info("A");
- semaphoreAB.release();
- }
- }
-
- @AllArgsConstructor
- static class B implements Runnable {
- private Semaphore semaphoreAB;
- private Semaphore semaphoreBC;
-
- @SneakyThrows
- @Override
- public void run() {
- // semaphore 信号量-1,总数为0的时候会等待
- semaphoreAB.acquire();
- log.info("B");
- // semaphore 信号量+1
- semaphoreBC.release();
- }
- }
-
- @AllArgsConstructor
- static class C implements Runnable {
- private Semaphore semaphoreBC;
-
- @SneakyThrows
- @Override
- public void run() {
- semaphoreBC.acquire();
- log.info("C");
- }
- }
- }
通过FutureTask的阻塞特性直接实现线程的顺序执行,这种方式比较简单,有点类似于单线程池执行。执行结果仍然是A->B->C。
代码如下:
- @Slf4j
- public class ThreadABC {
- /**
- * FutureTask 阻塞特性实现
- */
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- ExecutorService executorService = Executors.newFixedThreadPool(3);
-
- // 实际上 ExecutorService.submit 还是用的 FutureTask
- Object a = executorService.submit(new Runnable() {
- @SneakyThrows
- @Override
- public void run() {
- log.info("A");
- }
- }).get();
-
- Object b = executorService.submit(new Runnable() {
- @SneakyThrows
- @Override
- public void run() {
- log.info("B");
- }
- }).get();
-
- Object c = executorService.submit(new Runnable() {
- @SneakyThrows
- @Override
- public void run() {
- log.info("C");
- }
- }).get();
-
- log.info("main thread done.");
-
- System.exit(0);
- }
- }
JDK1.8中 CompletableFuture提供了非常强大的Future的扩展功能,简化异步编程。提供函数式编程的能力,可帮助我们完成复杂的线程的阶段行编程(CompletionStage)。具体这里不详细介绍,不了解的朋友可以去网上查一下资料。
执行代码:
- @Slf4j
- public class ThreadABC {
-
- /**
- * CompletableFuture (推荐)
- * JDK1.8中 CompletableFuture提供了非常强大的Future的扩展功能,简化异步编程,
- * 提供函数式编程的能力,可帮助我们完成复杂的线程的阶段行编程(CompletionStage)
- * {@link java.util.concurrent.CompletableFuture}
- */
- public static void main(String[] args) throws InterruptedException {
- ExecutorService executorService = Executors.newFixedThreadPool(3);
-
- // 有a,b,c三个线程(任务)
- Runnable a = () -> log.info("A");
- Runnable b = () -> log.info("B");
- Runnable c = () -> log.info("C");
-
- // 异步执行
- CompletableFuture.runAsync(a, executorService).thenRun(b).thenRun(c);
-
- log.info("main thread.");
- // 停止接受新任务,当已有任务将执行完,关闭线程池
- executorService.shutdown();
- while (!executorService.isTerminated()) {
- // 等待所有线程执行完成
- }
- System.exit(0);
- }
- }
执行结果:
main thread.
A
B
C
多线程的执行顺序,在面试过程中经常会被问到。不了解的,可以收藏一下。笔者建议不了解CompletableFuture,可以去尝试写一些例子。功能真的很强大!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。