交替打印FooBar
题目来源:https://leetcode-cn.com/problems/print-foobar-alternately
两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。
设计修改程序,以确保 "foobar" 被输出 n 次。
题解:两个线程,一个输出完唤醒另一个线程,输出时等待上一个线程。有多种写法
写法一:使用java的Condition条件await和signal实现
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * 两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。 * <p> * 请设计修改程序,以确保 "foobar" 被输出 n 次。 * <p> * https://leetcode-cn.com/problems/print-foobar-alternately * * @author jy.cui * @version 1.0 * @date 2020/10/9 16:40 */ class FooBar { private int n; private ReentrantLock lock = new ReentrantLock(); private Condition conditionFoo = lock.newCondition(); private Condition conditionBar = lock.newCondition(); private volatile boolean isSignal = false; public FooBar(int n) { this.n = n; } public void foo(Runnable printFoo) throws InterruptedException { lock.lock(); // 注意Condition需要被lock包围 for (int i = 0; i < n; i++) { // printFoo.run() outputs "foo". Do not change or remove this line. printFoo.run(); conditionFoo.signal(); //释放信号 isSignal = true; //标记已释放信号 conditionBar.await(); } lock.unlock(); } public void bar(Runnable printBar) throws InterruptedException { lock.lock(); for (int i = 0; i < n; i++) { if(!isSignal){ // 没释放信号再锁 conditionFoo.await(); } // printBar.run() outputs "bar". Do not change or remove this line. printBar.run(); isSignal = false; //输出完回退标记 conditionBar.signal(); } lock.unlock(); } }
写法二:利用syncronized的对象wait和notify,交替等待另一个线程的输出,标记为判断此线程的输出状态进行阻塞。
/** * 交替打印FooBar * 两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。 * <p> * 请设计修改程序,以确保 "foobar" 被输出 n 次。 * <p> * https://leetcode-cn.com/problems/print-foobar-alternately * * @author jy.cui * @version 1.0 * @date 2020/10/9 16:40 */ class FooBar { private int n; private Object lock = new Object(); private volatile boolean barHandle = false; //bar输出控制 public FooBar(int n) { this.n = n; } public void foo(Runnable printFoo) throws InterruptedException { for (int i = 0; i < n; i++) { synchronized (lock){ if(barHandle){ // bar输出时等待 lock.wait(); } // printFoo.run() outputs "foo". Do not change or remove this line. printFoo.run(); lock.notify(); //唤醒bar线程 barHandle = true; // 标记唤醒bar输出 } } } public void bar(Runnable printBar) throws InterruptedException { for (int i = 0; i < n; i++) { synchronized (lock){ if(!barHandle){ //初始等待foo输出 lock.wait(); } // printBar.run() outputs "bar". Do not change or remove this line. printBar.run(); lock.notify(); //唤醒foo线程 barHandle = false; //恢复标记,控制foo输出 } } } }
写法三:无锁,标记互斥,根据全局标记让出时间片
class FooBar { private int n; private volatile boolean flag = false; public FooBar(int n) { this.n = n; } public void foo(Runnable printFoo) throws InterruptedException { for (int i = 0; i < n; i++) { while (flag){ Thread.yield(); } // printFoo.run() outputs "foo". Do not change or remove this line. printFoo.run(); flag = true; } } public void bar(Runnable printBar) throws InterruptedException { for (int i = 0; i < n; i++) { while (!flag){ Thread.yield(); } // printBar.run() outputs "bar". Do not change or remove this line. printBar.run(); flag = false; } } }
写法四:Semaphore信号量,分别等待其他线程唤醒
class FooBar { private int n; private Semaphore foo = new Semaphore(0); private Semaphore bar = new Semaphore(0); public FooBar(int n) { this.n = n; } public void foo(Runnable printFoo) throws InterruptedException { for (int i = 0; i < n; i++) { if(i != 0){ foo.acquire(); } // printFoo.run() outputs "foo". Do not change or remove this line. printFoo.run(); bar.release(); } } public void bar(Runnable printBar) throws InterruptedException { for (int i = 0; i < n; i++) { bar.acquire(); // printBar.run() outputs "bar". Do not change or remove this line. printBar.run(); foo.release(); } } }