08 两个线程交替打印121212...

来源:互联网 发布:淘宝c店直播运营 编辑:程序博客网 时间:2024/05/29 14:40

前言

唉, 忧伤..

问题描述

今天 无意间看到了这样一个问题, 两个线程, 一个打印”1”, 另外的一个打印”2”, 写出程序实现如下输出”121212…”

思路

这个问题, 我以前也没有碰到过, 看到这个问题, 我的第一想法是, 两个线程
1. 让线程2先wait, 然后 线程1执行打印业务之后, 叫醒线程2, 然后 自己也wait
2. 然后 再”线程1 ‘变成’ 线程2, 线程2 ‘变成’ 线程1”, 然后 在走上面的业务[这里 如此说明, 仅仅是为了突出两个线程的业务基本上是相似的, 因此 可以共用一份代码]
3. 周期性的执行

Code01
然后 我写出了我的第一个版本的实现, 如下 :

    // 两条线程, 打印出121212    public static void main(String[] args) {        MyThread t01 = new MyThread(1), t02 = new MyThread(t01, 2);        t01.other = t02;        t01.start();//      Tools.sleep(1 * 1000);        t02.start();    }    // MyThread    static class MyThread extends Thread {        MyThread other;        private Object lock;        private int output;        // 初始化        public MyThread(MyThread other, int output) {            this.other = other;            this.lock = new Object();            this.output = output;        }        public MyThread(int output) {            this(null, output);        }        @Override        public void run() {            if(output == 2) {                synchronized (lock) {                    try {                        lock.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }            while(true) {                System.out.println(output);                synchronized (other.lock) {                    other.lock.notify();                }                synchronized (lock) {                    try {                        lock.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        }    }

这样看似正确了, 但是 多线程的场景下面, 你永远不能使用单线程的思维来思考
呵呵 存在很明显的几个问题[当然 如果存在还有其他问题, 大家可以分享一下], 看到了么
1. 试想如下场景了, t1线程运行了, 执行到了”other.lock.notify()”, 然而 t2线程此时还没有被wait掉, 那么 此时是不是就构成了”死锁”, 在此场景下面, t1, t2两个线程都会wait掉, 而不会有其他的线程唤醒他们, 因此 程序就这样僵持下去了, 直到XXX
2. 另外的一个死锁的原因来了, 当前线程唤醒了另外的一个线程”other.lock.notify()”, 而执行到另外线程的notify的时候”other.lock.notify()”, 当前线程还没有走到wait这一步”lock.wait();”, 造成了死锁


难道说上面的思路存在问题嘛?? no 原因不在于此, 原因在于 实现的家伙太菜了 哈哈哈 就是我嘛

然后 之后的时候, 通过查阅资料[refer : http://jeasonjack.iteye.com/blog/1844512], 我看到了另外的一个版本的实现, 当然 思路和上面的思路是一样的, 存在一些不同的地方

Code02

    // 两条线程, 打印出121212    public static void main(String[] args) {        MyThread03 t01 = new MyThread03(1), t02 = new MyThread03(2);        t01.start();        t02.start();    }    // MyThread    static class MyThread03 extends Thread {        static Object lock = new Object();        private int output;        // 初始化        public MyThread03(int output) {            this.output = output;        }        // run        @Override        public void run() {            // 可能存在第二个线程还没有wait, 结果 第一个线程notify了lock上面等待的线程[为空], 从而 导致死锁            // 一个不推荐, 但是 又想不到其他的好方法了, Thread.sleep() 控制流程, 以后 再回来想吧            if(output == 1) {                Tools.sleep(1 * 1000);            }            if(output == 2) {                synchronized (lock) {                    try {                        lock.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }            while(true) {                synchronized (lock) {                    System.out.println(output);                    lock.notify();                    try {                        lock.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        }    }

这样, 程序就”正常”了
对比一下 上面的存在的两个问题, 第一个问题, 被”Tools.sleep(1 * 1000);”, “解决”掉了, 第二个问题, 因为使用的是用一个对象的锁, 而且 未申请此对象的锁等待的线程仅此一个, 因此 不存在上面的第二个问题

不过 这里要说明的是, 使用”Thread.sleep(long millis) “来控制程序的流程, 不提倡使用, 请慎用

我记得tij中有两个编写java程序的原则, 一个是永远不要通过线程的优先级来控制程序的流程, 还有一个是什么忘记了 [刚才去翻了一下当时做的记录, 也没有找到, 管它的呢, 先记录在这里吧, 以后 找到了再填充回来]


然后, 在分享一个使用一个标志变量 + 忙等, 来实现的方法
refer : http://bbs.csdn.net/topics/320132441

Code03

    // 两条线程, 打印出121212    public static void main(String[] args) {        MyThread02 t01 = new MyThread02(1), t02 = new MyThread02(2);        t01.start();        t02.start();    }    // MyThread    static class MyThread02 extends Thread {        static volatile boolean isPrint1 = true;        private int output;        // 初始化        public MyThread02(int output) {            this.output = output;        }        // run        @Override        public void run() {            while(true) {                if(isPrint1) {                    if(output == 1) {                        System.out.println(output);                        isPrint1 = ! isPrint1;                    }                } else {                    if(output == 2) {                        System.out.println(output);                        isPrint1 = ! isPrint1;                    }                }            }        }    }

看到了吧, 实现也是非常的简洁呢, 使用isPrint1为volitile, 来保证了其他线程的可见性, 当isPrint1为true的时候, 线程1执行打印业务, 线程2什么都不干, 继续进入下一个循环, 当isPring为false的时候, 线程1什么都不干, 进入下一个循环, 线程2执行打印业务

相对的来说, 这个效率应该是比较高的吧, 毕竟业务太简单了

总结

啊 吃饭了, 写了一个小时, 好累啊

参考
线程交替执行?? –2016.08.09
http://bbs.csdn.net/topics/320132441
http://jeasonjack.iteye.com/blog/1844512

注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!

0 0
原创粉丝点击