LeetCode1114题。该题主要解决多线程执行顺序问题。java
咱们提供了一个类:web
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() 方法以后被执行。svg
- 示例 1:
- 输入: [1,2,3]
- 输出: “onetwothree”
- 解释:
有三个线程会被异步启动。
输入 [1,2,3] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 two() 方法,>线程 C 将会调用 three() 方法。
正确的输出是 “onetwothree”。- 示例 2:
- 输入: [1,3,2]
- 输出: “onetwothree”
- 解释:
输入 [1,3,2] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 three() 方法,线程 C 将会调用 two() 方法。
正确的输出是 “onetwothree”。注意:
尽管输入中的数字彷佛暗示了顺序,可是咱们并不保证线程在操做系统中的调度顺序。
你看到的输入格式主要是为了确保测试的全面性函数
解题思路有几个要点:工具
下面列出三种方法,我的推荐Semaphore方法,直观易懂。测试
Semaphore
与下一个CountDownLatch
类似,不一样的地方在于Semaphore
的值被获取到后是能够释放的,并不像CountDownLatch
那样一直减到底ui
得到Semaphore
的线程处理完它的逻辑以后,你就能够调用它的Release()
函数将它的计数器从新加1,这样其它被阻塞的线程就能够获得调用了this
public class Foo03 { //声明两个 Semaphore变量 private Semaphore spa,spb; public Foo03() { //初始化Semaphore为0的缘由:若是这个Semaphore为零,若是另外一线程调用(acquire)这个Semaphore就会产生阻塞,即可以控制second和third线程的执行 spa = new Semaphore(0); spb = new Semaphore(0); } public void first(Runnable printFirst) throws InterruptedException { // printFirst.run() outputs "first". Do not change or remove this line. printFirst.run(); //只有等first线程释放Semaphore后使Semaphore值为1,另一个线程才能够调用(acquire) spa.release(); } public void second(Runnable printSecond) throws InterruptedException { //只有spa为1才能执行acquire,若是为0就会产生阻塞 spa.acquire(); // printSecond.run() outputs "second". Do not change or remove this line. printSecond.run(); spb.release(); } public void third(Runnable printThird) throws InterruptedException { //只有spb为1才能经过,若是为0就会阻塞 spb.acquire(); // printThird.run() outputs "third". Do not change or remove this line. printThird.run(); } }
该方法与Semaphore
相似,直接借用线程工具类、设置吞吐量,控制后置进程的触发时机。
public class Foo { //声明两个 CountDownLatch变量 private CountDownLatch countDownLatch01, countDownLatch02; public Foo() { //初始化每一个CountDownLatch的值为1,表示有一个线程执行完后,执行等待的线程 countDownLatch01 = new CountDownLatch(1); countDownLatch02 = new CountDownLatch(1); } public void first(Runnable printFirst) throws InterruptedException { //当前只有first线程没有任何的阻碍,其他两个线程都处于等待阶段 // printFirst.run() outputs "first". Do not change or remove this line. printFirst.run(); //直到CountDownLatch01里面计数为0才执行因调用该countDownCatch01.await()而等待的线程 countDownLatch01.countDown(); } public void second(Runnable printSecond) throws InterruptedException { //只有countDownLatch01为0才能经过,不然会一直阻塞 countDownLatch01.await(); // printSecond.run() outputs "second". Do not change or remove this line. printSecond.run(); //直到CountDownLatch02里面计数为0才执行因调用该countDownCatch02.await()而等待的线程 countDownLatch02.countDown(); } public void third(Runnable printThird) throws InterruptedException { //只有countDownLatch02为0才能经过,不然会一直阻塞 countDownLatch02.await(); // printThird.run() outputs "third". Do not change or remove this line. printThird.run(); } }
该方法实现代码较多、逻辑复杂。实际上Semaphore
底层采用近似机制。
public class Foo { //控制变量 private int flag = 0; //定义Object对象为锁 private Object lock = new Object(); public Foo() { } public void first(Runnable printFirst) throws InterruptedException { synchronized (lock){ //若是flag不为0则让first线程等待,while循环控制first线程若是不满住条件就一直在while代码块中,防止出现中途跳入,执行下面的代码,其他线程while循环同理 while( flag != 0){ lock.wait(); } // printFirst.run() outputs "first". Do not change or remove this line. printFirst.run(); //定义成员变量为 1 flag = 1; //唤醒其他全部的线程 lock.notifyAll(); } } public void second(Runnable printSecond) throws InterruptedException { synchronized (lock){ //若是成员变量不为1则让二号等待 while (flag != 1){ lock.wait(); } // printSecond.run() outputs "second". Do not change or remove this line. printSecond.run(); //若是成员变量为 1 ,则表明first线程刚执行完,因此执行second,而且改变成员变量为 2 flag = 2; //唤醒其他全部的线程 lock.notifyAll(); } } public void third(Runnable printThird) throws InterruptedException { synchronized (lock){ //若是flag不等于2 则一直处于等待的状态 while (flag != 2){ lock.wait(); } // printThird.run() outputs "third". Do not change or remove this line. //若是成员变量为 2 ,则表明second线程刚执行完,因此执行third,而且改变成员变量为 0 printThird.run(); flag = 0; lock.notifyAll(); } } }
以上。感谢您的阅读。