让线程顺序运行的11种方法

这篇具有很好参考价值的文章主要介绍了让线程顺序运行的11种方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、让线程顺序运行的11种方法

1 方法说明

  1. 使用线程的join方法
  2. 使用主线程的join方法
  3. 使用线程的wait方法
  4. 使用线程的线程池方法
  5. 使用线程的Condition(条件变量)方法
  6. 使用CountDownLatch(倒计数)的方法
  7. 使用线程的CyclicBarrier(回环栅栏)方法
  8. 使用线程的Semaphore(信号量)方法
  9. 使用LockSupport的park与unpark方法
  10. 使用阻塞队列的put与take方法
  11. 使用CAS思想来完成多线程的顺序执行AtomicReference<Thread>

2 实现

2.1 使用线程的join方法

**join()😗*是Theard的方法,作用是调用线程需等待该join()线程执行完成后,才能继续用下运行。

**应用场景:**当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法。

2.1.1 实现代码
package cn.lyf.leetcode.thread.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

/**
 * @author lyf
 * @version 1.0
 * @classname ThreadDemo1
 * @description 方法说明
 *
 * 
 * 演示: 1、使用线程的join方法
 * join():是Theard的方法,作用是调用线程需等待该join()线程执行完成后,才能继续用下运行。
 * 
 * 应用场景:当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法。
 *
 * @since 2023/4/11 9:38
 */
@Slf4j
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
        }, "thread-a");

        Thread threadB = new Thread(() -> {
            try {
                threadA.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
        }, "thread-b");

        Thread threadC = new Thread(() -> {
            try {
                threadB.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
        }, "thread-c");

        threadA.start();
        threadB.start();
        threadC.start();
    }

    /**
     * 让线程睡眠timeOut 毫秒
     *
     * @param timeOut 单位毫秒
     */
    public static void sleep(long timeOut) {
        sleep(timeOut, TimeUnit.MILLISECONDS);
    }

    /**
     * 让线程睡眠
     *
     * @param timeOut 睡眠时间
     * @param unit    睡眠时间单位
     */
    public static void sleep(long timeOut, TimeUnit unit) {
        try {
            TimeUnit.MILLISECONDS.sleep(unit.toMillis(timeOut));
        } catch (InterruptedException e) {
            log.error("", e);
        }
    }
}

2.1.2 运行结果
2023-04-11 09:48:23 [ thread-a ]  INFO ThreadDemo1:39 - start...
2023-04-11 09:48:23 [ thread-a ]  INFO ThreadDemo1:42 - end...
2023-04-11 09:48:23 [ thread-b ]  INFO ThreadDemo1:51 - start...
2023-04-11 09:48:23 [ thread-b ]  INFO ThreadDemo1:54 - end...
2023-04-11 09:48:23 [ thread-c ]  INFO ThreadDemo1:63 - start...
2023-04-11 09:48:23 [ thread-c ]  INFO ThreadDemo1:66 - end...

2.2 使用主线程的join方法

在主线程中调用线程的join方法

2.2.1 实现代码
package cn.lyf.leetcode.thread.demo;

import lombok.extern.slf4j.Slf4j;

import static cn.lyf.leetcode.thread.demo.ThreadDemo1.sleep;

/**
 * @author lyf
 * @version 1.0
 * @classname ThreadDemo2
 * @description 方法说明
 *
 * 2. 使用主线程的join方法
 *
 * @since 2023/4/11 9:38
 */
@Slf4j
public class ThreadDemo2 {
    public static void main(String[] args) throws Exception {
        Thread threadA = new Thread(() -> {
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
        }, "thread-a");

        Thread threadB = new Thread(() -> {
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
        }, "thread-b");

        Thread threadC = new Thread(() -> {
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
        }, "thread-c");


        threadA.start();
        threadA.join();
        threadB.start();
        threadB.join();
        threadC.start();
    }
}

2.2.2 运行结果
2023-04-11 09:54:07 [ thread-a ]  INFO ThreadDemo2:36 - start...
2023-04-11 09:54:07 [ thread-a ]  INFO ThreadDemo2:39 - end...
2023-04-11 09:54:07 [ thread-b ]  INFO ThreadDemo2:43 - start...
2023-04-11 09:54:07 [ thread-b ]  INFO ThreadDemo2:46 - end...
2023-04-11 09:54:07 [ thread-c ]  INFO ThreadDemo2:50 - start...
2023-04-11 09:54:07 [ thread-c ]  INFO ThreadDemo2:53 - end...

2.3 使用线程的wait方法

2.3.1 实现代码
package cn.lyf.leetcode.thread.demo;

import lombok.extern.slf4j.Slf4j;

import static cn.lyf.leetcode.thread.demo.ThreadDemo1.sleep;

/**
 * @author lyf
 * @version 1.0
 * @classname ThreadDemo3
 * @description 方法说明
 *
 * 
 * 3. 使用线程的wait方法
 *
 * @since 2023/4/11 9:38
 */
@Slf4j
public class ThreadDemo3 {
    static final Object LOCK1 = new Object();
    static final Object LOCK2 = new Object();
    /**
     * 为什么要加这两个标识状态?
     * 如果没有状态标识,当thread-a已经运行完了thread-b才运行,
     * thread-b在等待thread-a唤醒导致thread-b永远处于等待状态
     */
    static boolean isRun1 = false;
    static boolean isRun2 = false;

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (LOCK1) {
                log.info("start...");
                sleep(100);
                log.info("end...");
                isRun1 = true;
                LOCK1.notify();
            }
        }, "thread-a").start();

        new Thread(() -> {
            synchronized (LOCK1) {
                while (!isRun1) {
                    try {
                        LOCK1.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                synchronized (LOCK2) {
                    log.info("start...");
                    sleep(100);
                    log.info("end...");
                    isRun2 = true;
                    LOCK2.notify();
                }
            }
        }, "thread-b").start();

        new Thread(() -> {
            synchronized (LOCK2) {
                while (!isRun2) {
                    try {
                        LOCK2.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.info("start...");
                sleep(100);
                log.info("end...");
            }
        }, "thread-c").start();
    }
}

2.3.2 运行结果
2023-04-11 10:09:54 [ thread-a ]  INFO ThreadDemo3:47 - start...
2023-04-11 10:09:54 [ thread-a ]  INFO ThreadDemo3:49 - end...
2023-04-11 10:09:54 [ thread-b ]  INFO ThreadDemo3:65 - start...
2023-04-11 10:09:54 [ thread-b ]  INFO ThreadDemo3:67 - end...
2023-04-11 10:09:54 [ thread-c ]  INFO ThreadDemo3:83 - start...
2023-04-11 10:09:54 [ thread-c ]  INFO ThreadDemo3:85 - end...

2.4 使用线程的线程池方法

JAVA通过Executors提供了四种线程池

  • 单线程化线程池(newSingleThreadExecutor);
  • 可控最大并发数线程池(newFixedThreadPool);
  • 可回收缓存线程池(newCachedThreadPool);
  • 支持定时与周期性任务的线程池(newScheduledThreadPool)。

**单线程化线程池(newSingleThreadExecutor)😗*优点,串行执行所有任务。

**submit():**提交任务。

**shutdown():**方法用来关闭线程池,拒绝新任务。

**应用场景:**串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2.4.1 实现代码
package cn.lyf.leetcode.thread.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static cn.lyf.leetcode.thread.demo.ThreadDemo1.sleep;

/**
 * @author lyf
 * @version 1.0
 * @classname ThreadDemo1
 * @description 方法说明
 *
 * 4. 使用线程的线程池方法
 *
 * @since 2023/4/11 9:38
 */
@Slf4j
public class ThreadDemo4 {
    public static void main(String[] args) {
        ExecutorService poolExecutor = Executors.newSingleThreadExecutor();

        poolExecutor.submit(() -> {
            log.info("threadA start...");
            sleep(100);
            log.info("threadA end...");
        });

        poolExecutor.submit(() -> {
            log.info("threadB start...");
            sleep(100);
            log.info("threadB end...");
        });

        poolExecutor.submit(() -> {
            log.info("threadC start...");
            sleep(100);
            log.info("threadC end...");
        });

        poolExecutor.shutdown();
    }
}

2.4.2 运行结果
2023-04-11 10:27:11 [ pool-1-thread-1 ]  INFO ThreadDemo4:41 - threadA start...
2023-04-11 10:27:11 [ pool-1-thread-1 ]  INFO ThreadDemo4:43 - threadA end...
2023-04-11 10:27:11 [ pool-1-thread-1 ]  INFO ThreadDemo4:47 - threadB start...
2023-04-11 10:27:11 [ pool-1-thread-1 ]  INFO ThreadDemo4:49 - threadB end...
2023-04-11 10:27:11 [ pool-1-thread-1 ]  INFO ThreadDemo4:53 - threadC start...
2023-04-11 10:27:11 [ pool-1-thread-1 ]  INFO ThreadDemo4:55 - threadC end...

2.5 使用线程的Condition(条件变量)方法

**Condition(条件变量)😗*通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例。

  • Condition中await()方法类似于Object类中的wait()方法。
  • Condition中await(long time,TimeUnit unit)方法类似于Object类中的wait(long time)方法。
  • Condition中signal()方法类似于Object类中的notify()方法。
  • Condition中signalAll()方法类似于Object类中的notifyAll()方法。

**应用场景:**Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一起等待某个条件(Condition),只有当该条件具备( signal 或者 signalAll方法被调用)时 ,这些等待线程才会被唤醒,从而重新争夺锁。

2.5.1 实现代码
package cn.lyf.leetcode.thread.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static cn.lyf.leetcode.thread.demo.ThreadDemo1.sleep;

/**
 * @author lyf
 * @version 1.0
 * @classname ThreadDemo5
 * @description 方法说明
 * 
 * 5.使用线程的Condition(条件变量)方法
 *
 * @since 2023/4/11 9:38
 */
@Slf4j
public class ThreadDemo5 {
    static final Lock LOCK = new ReentrantLock();
    static final Condition CONDITION_A = LOCK.newCondition();
    static final Condition CONDITION_B = LOCK.newCondition();
    static boolean isRunA = false;
    static boolean isRunB = false;

    public static void main(String[] args) {
        new Thread(() -> {
            // 加锁
            LOCK.lock();
            log.info("start...");
            try {
                sleep(100);
            } catch (Exception e) {
                log.error("", e);
            } finally {
                isRunA = true;
                CONDITION_A.signal();
                // 解锁
                LOCK.unlock();
                log.info("end...");
            }
        }, "thread-a").start();

        new Thread(() -> {
            // 加锁
            LOCK.lock();
            while (!isRunA) {
                try {
                    CONDITION_A.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("start...");
            try {
                sleep(100);
            } catch (Exception e) {
                log.error("", e);
            } finally {
                isRunB = true;
                CONDITION_B.signal();
                // 解锁
                LOCK.unlock();
                log.info("end...");
            }
        }, "thread-b").start();

        new Thread(() -> {
            // 加锁
            LOCK.lock();
            while (!isRunB) { // 这里根据状态码,来使thread-c处于一直等待状态
                try {
                    CONDITION_B.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("start...");
            try {
                sleep(100);
            } catch (Exception e) {
                log.error("", e);
            } finally {
                // 解锁
                LOCK.unlock();
                log.info("end...");
            }
        }, "thread-c").start();
    }
}

2.5.2 运行结果
2023-04-11 10:34:06 [ thread-a ]  INFO ThreadDemo5:48 - start...
2023-04-11 10:34:06 [ thread-a ]  INFO ThreadDemo5:58 - end...
2023-04-11 10:34:06 [ thread-b ]  INFO ThreadDemo5:72 - start...
2023-04-11 10:34:06 [ thread-b ]  INFO ThreadDemo5:82 - end...
2023-04-11 10:34:06 [ thread-c ]  INFO ThreadDemo5:96 - start...
2023-04-11 10:34:06 [ thread-c ]  INFO ThreadDemo5:104 - end...

2.6 使用线程的CountDownLatch(倒计数)方法

**CountDownLatch:**位于java.util.concurrent包下,利用它可以实现类似计数器的功能。

**应用场景:**比如有一个任务C,它要等待其他任务A,B执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。

2.6.1 实现代码
package cn.lyf.leetcode.thread.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;

import static cn.lyf.leetcode.thread.demo.ThreadDemo1.sleep;

/**
 * @author lyf
 * @version 1.0
 * @classname ThreadDemo6
 * @description 方法说明
 *
 * 
 * 6. 使用CountDownLatch(倒计数)的方法
 *
 * @since 2023/4/11 9:38
 */
@Slf4j
public class ThreadDemo6 {
    public static void main(String[] args) {
        CountDownLatch countDownLatchA = new CountDownLatch(1);
        CountDownLatch countDownLatchB = new CountDownLatch(1);
        new Thread(() -> {
            log.info("start...");
            sleep(100);
            log.info("end...");
            countDownLatchA.countDown();
        }, "thread-a").start();

        new Thread(() -> {
            try {
                countDownLatchA.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("start...");
            sleep(100);
            log.info("end...");
            countDownLatchB.countDown();
        }, "thread-b").start();

        new Thread(() -> {
            try {
                countDownLatchB.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("start...");
            sleep(100);
            log.info("end...");
        }, "thread-c").start();
    }
}

2.6.2 运行结果
2023-04-11 10:37:24 [ thread-a ]  INFO ThreadDemo6:40 - start...
2023-04-11 10:37:24 [ thread-a ]  INFO ThreadDemo6:42 - end...
2023-04-11 10:37:24 [ thread-b ]  INFO ThreadDemo6:52 - start...
2023-04-11 10:37:25 [ thread-b ]  INFO ThreadDemo6:54 - end...
2023-04-11 10:37:25 [ thread-c ]  INFO ThreadDemo6:64 - start...
2023-04-11 10:37:25 [ thread-c ]  INFO ThreadDemo6:66 - end...

2.7 使用线程的CyclicBarrier(回环栅栏)方法

**CyclicBarrier(回环栅栏)😗*通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。

**应用场景:**公司组织春游,等待所有的员工到达集合地点才能出发,每个人到达后进入barrier状态。都到达后,唤起大家一起出发去旅行。

2.7.1 实现代码
package cn.lyf.leetcode.thread.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

import static cn.lyf.leetcode.thread.demo.ThreadDemo1.sleep;

/**
 * @author lyf
 * @version 1.0
 * @classname ThreadDemo7
 * @description 方法说明
 *
 * 7. 使用线程的CyclicBarrier(回环栅栏)方法
 *
 * @since 2023/4/11 9:38
 */
@Slf4j
public class ThreadDemo7 {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);
        CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);
        new Thread(() -> {
            log.info("start...");
            sleep(100);
            log.info("end...");
            try {
                cyclicBarrier1.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }, "thread-a").start();

        new Thread(() -> {
            try {
                cyclicBarrier1.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            log.info("start...");
            sleep(100);
            log.info("end...");


            try {
                cyclicBarrier2.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }, "thread-b").start();

        new Thread(() -> {
            try {
                cyclicBarrier2.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            log.info("start...");
            sleep(100);
            log.info("end...");
        }, "thread-c").start();
    }
}

2.7.2 运行结果
2023-04-11 10:47:37 [ thread-a ]  INFO ThreadDemo7:41 - start...
2023-04-11 10:47:37 [ thread-a ]  INFO ThreadDemo7:43 - end...
2023-04-11 10:47:37 [ thread-b ]  INFO ThreadDemo7:57 - start...
2023-04-11 10:47:38 [ thread-b ]  INFO ThreadDemo7:59 - end...
2023-04-11 10:47:38 [ thread-c ]  INFO ThreadDemo7:75 - start...
2023-04-11 10:47:38 [ thread-c ]  INFO ThreadDemo7:77 - end...

2.8 使用Sephmore(信号量)实现线程按顺序运行

**Sephmore(信号量)😗*Semaphore是一个计数信号量,从概念上将,Semaphore包含一组许可证,如果有需要的话,每个acquire()方法都会阻塞,直到获取一个可用的许可证,每个release()方法都会释放持有许可证的线程,并且归还Semaphore一个可用的许可证。然而,实际上并没有真实的许可证对象供线程使用,Semaphore只是对可用的数量进行管理维护。

**acquire()😗*当前线程尝试去阻塞的获取1个许可证,此过程是阻塞的,当前线程获取了1个可用的许可证,则会停止等待,继续执行。

**release()😗*当前线程释放1个可用的许可证。

**应用场景:**Semaphore可以用来做流量分流,特别是对公共资源有限的场景,比如数据库连接。假设有这个的需求,读取几万个文件的数据到数据库中,由于文件读取是IO密集型任务,可以启动几十个线程并发读取,但是数据库连接数只有10个,这时就必须控制最多只有10个线程能够拿到数据库连接进行操作。这个时候,就可以使用Semaphore做流量控制。

2.8.1 实现代码
package cn.lyf.leetcode.thread.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Semaphore;

import static cn.lyf.leetcode.thread.demo.ThreadDemo1.sleep;

/**
 * @author lyf
 * @version 1.0
 * @classname ThreadDemo8
 * @description 方法说明
 * 
 * 8. 使用线程的Semaphore(信号量)方法
 *
 * @since 2023/4/11 9:38
 */
@Slf4j
public class ThreadDemo8 {
    public static void main(String[] args) {
        // 这里一定要将permits设置为0,只有设置为0 thread-a才会先执行,thread-b才会后执行
        Semaphore semaphore1 = new Semaphore(0);
        // 同上
        Semaphore semaphore2 = new Semaphore(0);
        new Thread(() -> {
            log.info("start...");
            sleep(100);
            log.info("end...");
            semaphore1.release();
        }, "thread-a").start();

        new Thread(() -> {
            try {
                semaphore1.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("start...");
            sleep(100);
            log.info("end...");
            semaphore1.release();
            semaphore2.release();
        }, "thread-b").start();

        new Thread(() -> {
            try {
                semaphore2.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("start...");
            sleep(100);
            log.info("end...");
            semaphore2.release();
        }, "thread-c").start();
    }
}

2.8.2 运行结果
2023-04-11 11:00:58 [ thread-a ]  INFO ThreadDemo8:42 - start...
2023-04-11 11:00:58 [ thread-a ]  INFO ThreadDemo8:44 - end...
2023-04-11 11:00:58 [ thread-b ]  INFO ThreadDemo8:54 - start...
2023-04-11 11:00:58 [ thread-b ]  INFO ThreadDemo8:56 - end...
2023-04-11 11:00:58 [ thread-c ]  INFO ThreadDemo8:67 - start...
2023-04-11 11:00:58 [ thread-c ]  INFO ThreadDemo8:69 - end...

2.9 使用LockSupport的park与unpark方法

java.util.concurrent.locks.LockSupport#park()

java.util.concurrent.locks.LockSupport#unpark(Thread thread)

  • park:方法是使当前线程暂停运行
  • unpark:方法是使指定线程恢复运行
2.9.1 实现代码
package cn.lyf.leetcode.thread.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.LockSupport;

import static cn.lyf.leetcode.thread.demo.ThreadDemo1.sleep;

/**
 * @author lyf
 * @version 1.0
 * @classname ThreadDemo9
 * @description 方法说明
 * 
 * 9. LockSupport
 * @since 2023/4/11 9:38
 */
@Slf4j
public class ThreadDemo9 {
    /**
     * 这里加标识,是为了保证threadA 在Unpark 时,ThreadB 已经处于Park状态了
     */
    static volatile boolean isRunA = false;

    /**
     * 这里加标识,是为了保证threadB 在Unpark 时,ThreadC 已经处于Park状态了
     */
    static volatile boolean isRunB = false;

    public static void main(String[] args) {

        Thread threadC = new Thread(() -> {
            while (!isRunB) {
                LockSupport.park();
            }
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
        }, "thread-c");

        Thread threadB = new Thread(() -> {
            while (!isRunA) {
                LockSupport.park();
            }
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
            isRunB = true;
            LockSupport.unpark(threadC);
        }, "thread-b");


        Thread threadA = new Thread(() -> {
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
            isRunA = true;
            LockSupport.unpark(threadB);
        }, "thread-a");

        threadC.start();
        threadB.start();
        threadA.start();
    }
}

2.9.2 运行结果
2023-04-11 11:25:20 [ thread-a ]  INFO ThreadDemo9:56 - start...
2023-04-11 11:25:20 [ thread-a ]  INFO ThreadDemo9:59 - end...
2023-04-11 11:25:20 [ thread-b ]  INFO ThreadDemo9:46 - start...
2023-04-11 11:25:20 [ thread-b ]  INFO ThreadDemo9:49 - end...
2023-04-11 11:25:20 [ thread-c ]  INFO ThreadDemo9:36 - start...
2023-04-11 11:25:20 [ thread-c ]  INFO ThreadDemo9:39 - end...

2.10 使用阻塞队列的put与take方法

阻塞队列的常用API

方法类型 抛出异常 特殊值 阻塞 超时
插入 add(e) offer(e) put(e) offer(e, timeOut, unit)
移除 remove() poll() take() poll(timeOut, unit)
检查 element() peek() 不可用 不可用

本例主要是使用take操作的阻塞作用

2.10.1 实现代码
package cn.lyf.leetcode.thread.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import static cn.lyf.leetcode.thread.demo.ThreadDemo1.sleep;

/**
 * @author lyf
 * @version 1.0
 * @classname ThreadDemo10
 * @description 方法说明
 * 
 * 10. 阻塞队列来实现
 * @since 2023/4/11 9:38
 */
@Slf4j
public class ThreadDemo10 {

    static volatile boolean isRunA = false;

    static volatile boolean isRunB = false;

    static BlockingQueue<Object> queue1 = new LinkedBlockingQueue<>();
    static BlockingQueue<Object> queue2 = new LinkedBlockingQueue<>();

    public static void main(String[] args) {
        final Object obj = new Object();
        Thread threadA = new Thread(() -> {
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
            isRunA = true;
            try {
                queue1.put(obj);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread-a");

        Thread threadB = new Thread(() -> {
            while (!isRunA) {
                try {
                    queue1.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
            isRunB = true;
            try {
                queue2.put(obj);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }, "thread-b");

        Thread threadC = new Thread(() -> {
            while (!isRunB) {
                try {
                    queue2.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
        }, "thread-c");

        threadC.start();
        threadB.start();
        threadA.start();
    }
}

2.10.2 运行结果
2023-04-11 12:19:10 [ thread-a ]  INFO ThreadDemo10:32 - start...
2023-04-11 12:19:10 [ thread-a ]  INFO ThreadDemo10:35 - end...
2023-04-11 12:19:10 [ thread-b ]  INFO ThreadDemo10:52 - start...
2023-04-11 12:19:10 [ thread-b ]  INFO ThreadDemo10:55 - end...
2023-04-11 12:19:10 [ thread-c ]  INFO ThreadDemo10:73 - start...
2023-04-11 12:19:10 [ thread-c ]  INFO ThreadDemo10:76 - end...

2.11 使用CAS思想来实现线程的顺序运行(AtomicReference<Thread>

compare and swap的缩写,中文翻译成比较并交换, 实现并发算法时常用到的一种技术。它包含三个操作数——内存位置、预期原值及更新值。

执行CAS操作的时候,将内存位置的值与预期原值比较:

如果相匹配,那么处理器会自动将该位置值更新为新值,

如果不匹配,处理器不做任何操作,多个线程同时执行CAS操作只有一个会成功文章来源地址https://www.toymoban.com/news/detail-648569.html

2.11.1 实现代码
package cn.lyf.leetcode.thread.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.atomic.AtomicReference;

import static cn.lyf.leetcode.thread.demo.ThreadDemo1.sleep;

/**
 * @author lyf
 * @version 1.0
 * @classname ThreadDemo11
 * @description 方法说明
 * <p>
 * 11. 使用CAS思想来完成多线程的顺序执行
 * @since 2023/4/11 9:38
 */
@Slf4j
public class ThreadDemo11 {
    public static void main(String[] args) {
        AtomicReference<Thread> threadAtomicReference = new AtomicReference<>();
        Thread threadA = new Thread(() -> {
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
            threadAtomicReference.set(Thread.currentThread());
        }, "thread-a");

        Thread threadB = new Thread(() -> {
            while (!threadAtomicReference.compareAndSet(threadA, null)) {
                sleep(1);
            }
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
            threadAtomicReference.set(Thread.currentThread());
        }, "thread-b");

        Thread threadC = new Thread(() -> {
            while (!threadAtomicReference.compareAndSet(threadB, null)) {
                sleep(1);
            }
            log.info("start...");
            // 模拟业务执行时长
            sleep(100);
            log.info("end...");
        }, "thread-c");

        threadC.start();
        threadB.start();
        threadA.start();
    }
}

2.11.2 运行结果
2023-04-11 14:06:32 [ thread-a ]  INFO ThreadDemo11:23 - start...
2023-04-11 14:06:32 [ thread-a ]  INFO ThreadDemo11:26 - end...
2023-04-11 14:06:32 [ thread-b ]  INFO ThreadDemo11:34 - start...
2023-04-11 14:06:32 [ thread-b ]  INFO ThreadDemo11:37 - end...
2023-04-11 14:06:32 [ thread-c ]  INFO ThreadDemo11:45 - start...
2023-04-11 14:06:33 [ thread-c ]  INFO ThreadDemo11:48 - end...

3 参考文章

  • 多线程-顺序执行

到了这里,关于让线程顺序运行的11种方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • c++中 多线程执行时 线程的执行顺序不固定

            C++中多线程执行时,线程的执行顺序是不确定的。这是由于 多线程的并发性质 导致的。         在多线程程序中,多个线程可以同时执行,并且它们的执行顺序是由系统调度器决定的。系统调度器根据各种因素(如线程的优先级、线程的状态等)来决定何时

    2024年02月08日
    浏览(26)
  • 线程按顺序循环执行

    假设有3个线程,依次打印A、B、C,按顺序循环打印100次。 这个其实是线程通信,如果只是按顺序执行,用只有一个线程的线程池,依次提交线程任务就行,但是这里还不是每个线程只执行一次,需要循环重复打印。 这里有两种处理方式,一种是搞个全局int变量,对线程数取

    2024年02月04日
    浏览(30)
  • Flutter 单线程模型保证UI运行流畅

    Flutter 框架出色的渲染和交互能力。支撑起这些复杂的能力背后,实际上是基于单线程模型的 Dart。那么,与原生 Android 和 iOS 的多线程机制相比,单线程的 Dart 如何从语言设计层面和代码运行机制上保证 Flutter UI 的流畅性呢? 因此今天,我会通过几个小例子,循序渐进地向你

    2024年02月16日
    浏览(29)
  • c#让三个线程按照顺序执行

    三个线程都是while(true)的循环体 A线程:采集数据 B线程:画曲线 C线程:存数据库 AutoResetEvent 是一个线程同步的类,它提供了一种机制,允许一个或多个线程等待直到接收到信号,然后继续执行。 以下是 AutoResetEvent 的一些常用成员: WaitOne() :使当前线程等待接收信号。

    2024年02月02日
    浏览(25)
  • RabbitMQ如何保证顺序消费

    单一生产者:消息生产的顺序性仅支持单一生产者。 串行发送:如果生产者采用多线程并行发送,则不同线程产生的消息无法保证先后顺序。 假如生产者A发送消息1,生产者B发送消息2,生产者C发送消息3,由于是没办法保证顺序性,所以进入队列的顺序可能变成了3,1,2。

    2024年02月15日
    浏览(36)
  • RabbitMQ如何保证顺序性

    顺序性 : 消息的顺序性是指消费者消费到消息和发送者发布的消息的顺序是一致的 举个例子,不考虑消息重复的情况下,如果生产者发布的消息分别为msg1、msg2、msg3 那么消费者必然也是按照 msg1、msg2、msg3 的顺序来消费的 目前很多资料显示RabbitMQ消息能够保障顺序性,这是

    2024年02月13日
    浏览(26)
  • 如何保证Kafka顺序消费

    在Kafka中Partition(分区)是真正保存消息的地方,发送的消息都存放在这里。Partition(分区)又存在于Topic(主题)中,并且一个Topic(主题)可以指定多个Partition(分区)。 在Kafka中,只保证Partition(分区)内有序,不保证Topic所有分区都是有序的。 所以 Kafka 要保证消息的消费顺序,可

    2024年02月15日
    浏览(28)
  • 深入理解高并发编程 - 线程的执行顺序

    在Java中,线程的执行顺序是由操作系统的调度机制决定的,具体顺序是不确定的,取决于多个因素,如操作系统的调度策略、线程的优先级、线程的状态转换等。因此,不能对线程的执行顺序做出可靠的假设。 以下是一个简单的Java代码示例,演示了多个线程的执行顺序是不

    2024年02月14日
    浏览(42)
  • C# 多线程交替按照指定顺序执行

    1.关于AutoResetEvent和ManualResetEvent的区别解释如下: AutoResetEvent和ManualResetEvent是.NET中的两个线程同步类。它们之间的主要区别在于其释放信号的方式以及对等待线程的影响。 AutoResetEvent的作用是在等待的线程被信号唤醒后,将信号自动重置为非终止状态。也就是说,当一个线程

    2024年02月11日
    浏览(28)
  • mq如何保证消息顺序性

    面试的时候,经常会有面试官问道这个问题,发送顺序消息。 顺序性其实有两方面,一方面要保证Producer发送时是有序的,Consumer接受和处理消息的有序性。 另一面来说,我们也要考虑是需要全局有序还是局部有序就可以。 kafka的topic是分Partition的,当有多个Partition的时候,

    2024年02月09日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包