多线程交替循环打印ABC

业务需求

之前使用线程池时,更多的是关注执行的最终结果,至于每一个阶段到底是哪一个线程执行,其实我们并不关心,一般都是框架本身随机选取线程池;

 

现在有一个需求是交替循环打印,例如ABCABCABC......

 

解决方案

方案一

核心:synchronized关键字、Object类中的wait和notify方法

查看代码
/**
* 交替循环打印ABC 10次
* /
void contextLoads() {
    // 计数器
    private static int count = 0;
    // 锁
    private static final Object lock = new Object();

    Thread a = new Thread(() -> {
        for (int i = 1; i <= 10; i++) {
            // 申请获得锁
            synchronized (lock) {
                // 判断当前是否该打印
                // 特别强调:此处不能用if代替while!!!虽然当线程被唤醒时需要去重新获得锁,但代码是继续向下执行的,所以得循环
                while (count % 3 != 0) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("第" +  i + "次打印:" + "A");
                count++;
                // 唤醒所有正在等待的线程,最终只有一个线程能获得锁,其他线程继续等待
                lock.notifyAll();
            }
        }
    });

    Thread b = new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            synchronized (lock) {
                while (count % 3 != 1) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("B");
                count++;
                lock.notifyAll();
            }
        }
    });

    Thread c = new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            synchronized (lock) {
                while (count % 3 != 2) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("C");
                count++;
                lock.notifyAll();
            }
        }
    });

    // 启动线程
    a.start();
    b.start();
    c.start();
}

 

方案二

核心:ReentrantLock、Condition对象的await和signal方法

查看代码
 void contextLoads() {
        /**
         * 阻塞线程被唤醒后需要重新获取锁,只有在拿到锁后才能从上次停止的地方继续执行
         */
    
        private static int count = 0;

        private static final ReentrantLock lock = new ReentrantLock();

        Condition A = lock.newCondition();
        Condition B = lock.newCondition();
        Condition C = lock.newCondition();

        Thread a = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                lock.lock();
                while (count % 3 != 0) {
                    try {
                        // 注意阻塞的主体
                        A.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("第" +  i + "次打印:" + "A");
                count++;
                // 注意唤醒的主体
                B.signal();
                lock.unlock();
            }
        });

        Thread b = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                lock.lock();
                while (count % 3 != 1) {
                    try {
                        B.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("第" +  i + "次打印:" + "B");
                count++;
                C.signal();
                lock.unlock();
            }
        });

        Thread c = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                lock.lock();
                while (count % 3 != 2) {
                    try {
                        C.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("第" +  i + "次打印:" + "C");
                count++;
                A.signal();
                lock.unlock();
            }
        });
        
        // 启动线程
        a.start();
        b.start();
        c.start();
    }

 

参考链接

【1】三个线程如何交替打印ABC循环100次

posted @ 2025-03-18 09:51  先娶国王后取经  阅读(58)  评论(0)    收藏  举报