多线程之按顺序打印结果

前言

文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger
种一棵树最好的时间是十年前,其次是现在
我知道很多人不玩qq了,但是怀旧一下,欢迎加入六脉神剑Java菜鸟学习群,群聊号码:549684836 鼓励大家在技术的路上写博客
絮叨 这个是一个很简单的多线程问题,但是小六六一看觉得很简单,但是真正写起来也不是那么简单,说明小六六自己的代码还是写的太少了哈哈
题目 我们提供了一个类:
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 {privateint 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博客,先熟悉一下。这题应该算简单的题型了。
多线程之按顺序打印结果
文章图片

日常求赞
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是真粉。
创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见
六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !

    推荐阅读