题目
我们提供了一个类:
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(); } }