简述
面试中常常被问到多线程交替输出数字或字母序列的问题,例如AB两个线程交替输出1到100。三个线程交替输出abcabcabc...等
wait notify实现3线程交替输出abc
我们首先抽象出一个用于打印字母的类:
package ThreadTest.testOut; public class SyncWaitNotify { int flag; int loopNumber; public SyncWaitNotify(int flag, int loopNumber) { this.flag = flag; this.loopNumber = loopNumber; } public void print(int waitFlag,int nextFlag,String str){ synchronized (this){ while(waitFlag != nextFlag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(str); flag = nextFlag; this.notifyAll(); } } }
需要注意我们这里用整形flag的123代表abc,当前的flag不等于等待的flag时,将会进入等待队列等待。
当被唤醒后则则会更新标记并唤醒其余线程进行输出,线程运行代码:
package ThreadTest.testOut; public class TestABC { public static void main(String[] args) { SyncWaitNotify syncWaitNotify = new SyncWaitNotify(1, 5); new Thread(() -> { syncWaitNotify.print(1, 2, "a"); }).start(); new Thread(() -> { syncWaitNotify.print(2, 3, "b"); }).start(); new Thread(() -> { syncWaitNotify.print(3, 1, "c"); }).start(); } }
await singal实现交替输出abc
这个和wait notify不同之处在于,wait notify一唤醒就唤醒全部,醒来了再去判断自己该不该执行任务,不该的话就继续wait。而await singal有三个condiction,每个线程完成任务后可以知道该唤醒哪一个线程
package ThreadTest.testOut; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class TestABC2 { public static void main(String[] args) throws InterruptedException { AwaitSignal awaitSignal = new AwaitSignal(5); Condition a = awaitSignal.newCondition(); Condition b = awaitSignal.newCondition(); Condition c = awaitSignal.newCondition(); new Thread(()->{ awaitSignal.print("a",a,b); }).start(); new Thread(()->{ awaitSignal.print("b",b,c); }).start(); new Thread(()->{ awaitSignal.print("c",c,a); }).start(); Thread.sleep(1000); awaitSignal.lock(); try{ a.signal(); } finally { awaitSignal.unlock(); } } } class AwaitSignal extends ReentrantLock{ private int loopNumber; public AwaitSignal(int loopNumber){ this.loopNumber = loopNumber; } //参数1:打印内容 参数2:进入哪间休息室 参数3:下一间休息室 public void print(String str,Condition current,Condition next){ for(int i=0;i<loopNumber;i++){ lock(); try{ current.await(); System.out.print(str); next.signal();//唤醒下一间休息室 } catch (InterruptedException e) { e.printStackTrace(); } finally { unlock(); } } } }
park和unpark实现交替输出abc
我们还可以使用park unpark完成此任务,这种实现方式是以线程为单位的,所以代码更加简洁
package ThreadTest.testOut; import java.util.concurrent.locks.LockSupport; public class TestABC3 { static Thread t1; static Thread t2; static Thread t3; public static void main(String[] args) { ParkUnpark parkUnpark = new ParkUnpark(5); t1 = new Thread(()->{ parkUnpark.print("a",t2); }); t2 = new Thread(()->{ parkUnpark.print("b",t3); }); t3 = new Thread(()->{ parkUnpark.print("c",t1); }); t1.start(); t2.start(); t3.start(); LockSupport.unpark(t1); } } class ParkUnpark { private int loopNumber; public ParkUnpark(int loopNumber) { this.loopNumber = loopNumber; } public void print(String str,Thread next){ for(int i=0;i<loopNumber;i++){ LockSupport.park(); System.out.print(str); LockSupport.unpark(next); } } }