多线程篇-交替打印

背景

有如下需求:

线程1打印5次a,线程2打印5次b,线程3打印5次c,要求最后的结果是abcabcabcabc

步骤

简单情况

先考虑简单的情况,然后进行拓展。
简单版需求:

线程1打印1,线程2打印2,要求先打印2再打印1

我们先新建出线程1和线程2,代码如下:

新建线程
public static void main(String[] args) {
        Thread thread1 = new Thread(() -> log.info("1"), "t1");
        Thread thread2 = new Thread(() -> log.info("2"), "t2");
        thread1.start();
        thread2.start();
}

这里我们如果直接执行这个main方法的话,因为线程1先执行,所以更容易被cpu调度,所以结果大概率是1 2.但是,这个概率不是100%。我们的需求是先执行线程2,后执行线程1.考虑使用wait-notify实现。

wait-notify实现
public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock){
                while (!is2){
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("1");
            }
        }, "t1");
        Thread thread2 = new Thread(() -> {
            synchronized (lock){
                System.out.println("2");
                is2 = true;
                lock.notifyAll();
            }
        }, "t2");
        thread1.start();
        thread2.start();
}
第一步,给两个线程都加上锁,保证俩线程不可能同时执行,即每次只执行其中一个。

第二步,使用布尔值is2来标识线程2是否已经执行结束,如果结束了,则唤醒线程1.

这样就保证了先打印2再打印1.

这里有两个点需要注意:

1.为什么使用while,而不是if,这个是为了防止虚假唤醒。什么意思呢,notify-wait的机制导致notify的时候你不知道你唤醒的是哪个线程,也就是说线程1可能被其他的线程3唤醒,加个while,如果唤醒之后不满足条件,则继续等待。

2.为什么使用notifyall,原因与1一致。

复杂情况

搞清楚了两个怎么做,考虑怎么放到多个上。两个的时候关键是有布尔值做标记,如果多个的话就没办法通过布尔值标记每个的状态了,所以考虑使用int标记每个的状态。

复杂情况解法
public class WaitTest {

    static final Object lock = new Object();
    static boolean is2 = false;//2是否执行了
    static int flag = 1;
    static int runNumber = 5;

    //使用wait notify
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < runNumber; i++) {
                synchronized (lock){
                    while (flag != 1){
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print("a");
                    flag = 2;
                    lock.notifyAll();
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < runNumber; i++) {
                synchronized (lock){
                    while (flag != 2){
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print("b");
                    flag = 3;
                    lock.notifyAll();
                }
            }
        });
        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < runNumber; i++) {
                synchronized (lock){
                    while (flag != 3){
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print("c");
                    flag = 1;
                    lock.notifyAll();
                }
            }
        });
        thread1.start();
        thread2.start();
        thread3.start();
    }
}


上面的方法虽然可以打印出正确的结果,但是重复代码太多了,不够优雅,java是面向对象编程嘛,所以我们就想办法进行优化。

复杂情况解法优化
public class WaitTest {
//    static int flag = 1;
    static int runNumber = 5;
    static final Object lock = new Object();

    //使用wait notify
    public static void main(String[] args) {
        WaitNotify waitNotify = new WaitNotify(1);
        Thread thread1 = new Thread(() -> {
            waitNotify.print(runNumber,1, 2, "a");
        });
        Thread thread2 = new Thread(() -> {
            waitNotify.print(runNumber, 2, 3, "b");
        });
        Thread thread3 = new Thread(() -> {
            waitNotify.print(runNumber, 3, 1, "c");
        });
        thread1.start();
        thread2.start();
        thread3.start();
    }

}

class WaitNotify{
    static Object lock = new Object();
    private int flag;
    void print(int runNumber, int waitFlag, int nextFlag, String str){
        for (int i = 0; i < runNumber; i++) {
            synchronized (lock){
                while (flag != waitFlag){
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.print(str);
                flag = nextFlag;
                lock.notifyAll();
            }
        }
    }

    public WaitNotify(int flag) {
        this.flag = flag;
    }
}
posted @   盲从者列表  阅读(149)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示