多线程之按顺序打印结果

1,920 阅读5分钟

前言

文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820…
种一棵树最好的时间是十年前,其次是现在

絮叨

这个是一个很简单的多线程问题,但是小六六一看觉得很简单,但是真正写起来也不是那么简单,说明小六六自己的代码还是写的太少了哈哈

题目

我们提供了一个类:

public class Foo {
  public void one() { print("one"); }
  public void two() { print("two"); }
  public void three() { print("three"); }
}

三个不同的线程将会共用一个 Foo 实例。

  • 线程 A 将会调用 one() 方法
  • 线程 B 将会调用 two() 方法
  • 线程 C 将会调用 three() 方法

请设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。

示例 1:

输入: [1,2,3]
输出: "onetwothree"
解释: 
有三个线程会被异步启动。
输入 [1,2,3] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 two() 方法,线程 C 将会调用 three() 方法。
正确的输出是 "onetwothree"

题解一 synchronized 锁和控制变量

package com.code.thread;

/**
 * 上面方法就是采用一个synchronized wait notifyAll  这些来实现的,但是吧,还需要一个while去自旋,还得靠一个信号量,感觉有点low。
 */

class Foo {

    private  int flag=0;
    private Object lock=new Object();
    public Foo() {

    }

    public void first(Runnable printFirst) throws InterruptedException {
        synchronized(lock){
            while (flag!=0){
                lock.wait();
            }
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
            flag=1;
            lock.notifyAll();
        }

    }

    public void second(Runnable printSecond) throws InterruptedException {
        synchronized (lock) {
            while (flag != 1) {
                lock.wait();
            }
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            flag = 2;
            lock.notifyAll();
        }
    }

    public void third(Runnable printThird) throws InterruptedException {
        synchronized(lock) {
            while (flag != 2) {
                lock.wait();
            }
            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
            lock.notifyAll();
        }
    }

    public static void main(String[] args) throws Exception {
        final Foo foo = new Foo();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.first(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(1);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.second(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(2);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.third(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(3);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t3.start();
        t2.start();
        t1.start();

    }
}

上面方法就是采用一个synchronized wait notifyAll 这些来实现的,但是吧,还需要一个while去自旋,还得靠一个信号量,感觉有点low。

题解二 用CountDownLatch来控制

  • countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
  • 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数- 器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
package com.code.thread;

import java.util.concurrent.CountDownLatch;

public class Foo1 {
    //定义2个countDownLatch
    private CountDownLatch countDownLatchA=new CountDownLatch(1); //说明只要一个线程调用它就放行 ,它是到0就放行
    private CountDownLatch countDownLatchB=new CountDownLatch(1);
    public Foo1() {

    }

    public void first(Runnable printFirst) throws InterruptedException {
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();


    }

    public void second(Runnable printSecond) throws InterruptedException {
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();

    }

    public void third(Runnable printThird) throws InterruptedException {
            // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();

    }

    public static void main(String[] args) throws Exception {
        final Foo1 foo = new Foo1();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.first(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(1);
                        }
                    });
                    foo.countDownLatchA.countDown();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.countDownLatchA.await();
                    foo.second(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(2);
                        }
                    });
                    foo.countDownLatchB.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                    try {
                        foo.countDownLatchB.await();
                        foo.third(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(3);
                            }
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            }
        });
        t3.start();
        t2.start();
        t1.start();

    }
}

方法三 信号量

基于信号量的解题思路

  • Semaphore 是什么?
  • Semaphore 是一个计数信号量,必须由获取它的线程释放。
package com.code.thread;

import java.util.concurrent.Semaphore;

/**
 *
 * 基于信号量的解题思路
 * Semaphore 是什么?
 * Semaphore 是一个计数信号量,必须由获取它的线程释放。
 *
 * 常用于限制可以访问某些资源的线程数量,例如通过 Semaphore 限流。
 */
public class Foo2 {

    //初始化Semaphore为0的原因:
    // 如果这个Semaphore为零,如果另一线程调用(acquire)这个Semaphore就会产生阻塞,
    // 便可以控制second和third线程的执行
    private Semaphore spa=new Semaphore(0) ;
    private Semaphore spb=new Semaphore(0);




    public Foo2() {

    }

    public void first(Runnable printFirst) throws InterruptedException {

        printFirst.run();
        spa.release();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        // printSecond.run() outputs "second". Do not change or remove this line.
        spa.acquire();
        printSecond.run();
        spb.release();
    }

    public void third(Runnable printThird) throws InterruptedException {
        // printThird.run() outputs "third". Do not change or remove this line.
        spb.acquire();
        printThird.run();
    }

    public static void main(String[] args) throws Exception {
        final Foo2 foo = new Foo2();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.first(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(1);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.second(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(2);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.third(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(3);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t3.start();
        t2.start();
        t1.start();

    }
}

解题四 LockSupport

归根结底,LockSupport调用的Unsafe中的native代码:

  • public native void unpark(Thread jthread);
  • public native void park(boolean isAbsolute, long time);
  • 两个函数声明清楚地说明了操作对象:park函数是将当前Thread阻塞,而unpark函数则是将另一个Thread唤醒。
package com.code.thread;

import java.util.concurrent.locks.LockSupport;

/**'
 * 归根结底,LockSupport调用的Unsafe中的native代码:
 * public native void unpark(Thread jthread);
 * public native void park(boolean isAbsolute, long time);
 * 两个函数声明清楚地说明了操作对象:park函数是将当前Thread阻塞,而unpark函数则是将另一个Thread唤醒。
 *
 * 与Object类的wait/notify机制相比,park/unpark有两个优点:1. 以thread为操作对象更符合阻塞线程的直观定义;2. 操作更精准,可以准确地唤醒某一个线程(notify随机唤醒一个线程,notifyAll唤醒所有等待的线程),增加了灵活性。
 */
public class Foo3 {
    static Thread t1=null,t2=null,t3=null;

    public Foo3() {

    }

    public void first(Runnable printFirst) throws InterruptedException {

        printFirst.run();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        // printSecond.run() outputs "second". Do not change or remove this line.
        printSecond.run();
    }

    public void third(Runnable printThird) throws InterruptedException {
        // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();
    }


        public static void main(String[] args) throws Exception {
            final Foo3 foo = new Foo3();

             t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        foo.first(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(1);
                            }
                        });
                        LockSupport.unpark(t2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
             t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    LockSupport.park();
                    try {
                        foo.second(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(2);
                            }
                        });
                        LockSupport.unpark(t3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
             t3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    LockSupport.park();
                    try {
                        foo.third(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(3);
                            }
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t3.start();
            t2.start();
            t1.start();

    }
}

结尾

其实解法有很多,如果多线程不熟悉的小伙伴,可以去我的github博客,先熟悉一下。这题应该算简单的题型了。

日常求赞

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是真粉

创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见

六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !