Skip to Content

同步模式:顺序控制

固定运行顺序

必须先打印 2 ,后打印 1

wait notify 版

static final Object lock = new Object(); // 表示 t2 是否运行过 static boolean t2runned = false; public static void main(String[] args) { Thread t1 = new Thread(() -> { synchronized (lock) { // 如果t2线程没有运行过,就一直循环等待 while (!t2runned) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("1"); } }, "t1"); Thread t2 = new Thread(() -> { synchronized (lock) { log.debug("2"); t2runned = true; lock.notify(); } }, "t2"); t1.start(); t2.start(); }

Park Unpark 版

上述代码实现上很麻烦: 首先,需要保证先 wait 再 notify,否则 wait 线程永远得不到唤醒。因此使用了『运行标记』来判断该不该 wait 。 第二,如果有些干扰线程错误地 notify 了 wait 线程,条件不满足时还要重新等待,使用了 while 循环来解决此问题。 最后,唤醒对象上的 wait 线程需要使用 notifyAll,因为同步对象上的等待线程可能不止一个可以使用 LockSupport 类的 park 和 unpark 来简化上面的业务:

public static void main(String[] args) { Thread t1 = new Thread(() -> { LockSupport.park(); log.debug("1"); }, "t1"); t1.start(); new Thread(() -> { log.debug("2"); LockSupport.unpark(t1); },"t2").start(); }

park 和 unpark 方法比较灵活,他俩谁先调用,谁后调用无所谓。并且是以线程为单位进行暂停和恢复,不需要同步对象和运行标记。

交替输出

线程 1 输出 a 5 次,线程 2 输出 b 5 次,线程 3 输出 c 5 次。现在要求输出 abcabcabcabcabc 怎么实现

wait notify 版

class WaitNotify { // 打印 a 1 2 public void print(String str, int waitFlag, int nextFlag) { for (int i = 0; i < loopNumber; i++) { synchronized (this) { while (flag != waitFlag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.print(str); flag = nextFlag; this.notifyAll(); } } } // 等待标记 private int flag; // 2 // 循环次数 private int loopNumber; public WaitNotify(int flag, int loopNumber) { this.flag = flag; this.loopNumber = loopNumber; } }

启动线程并调用:

public static void main(String[] args) { WaitNotify wn = new WaitNotify(1, 5); new Thread(() -> { wn.print("a", 1, 2); }).start(); new Thread(() -> { wn.print("b", 2, 3); }).start(); new Thread(() -> { wn.print("c", 3, 1); }).start(); }

Lock 条件变量版

该实现没有考虑 a,b,c 线程都就绪再开始

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(); } } } }

启动线程并调用:

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 { System.out.println("开始..."); a.signal(); } finally { awaitSignal.unlock(); } }

Park Unpark 版

通过传递线程实现:

class ParkUnpark { public void print(String str, Thread next) { for (int i = 0; i < loopNumber; i++) { LockSupport.park(); System.out.print(str); LockSupport.unpark(next); } } private int loopNumber; public ParkUnpark(int loopNumber) { this.loopNumber = loopNumber; } }

启动线程并调用:

static Thread t1; static Thread t2; static Thread t3; public static void main(String[] args) { ParkUnpark pu = new ParkUnpark(5); t1 = new Thread(() -> { pu.print("a", t2); }); t2 = new Thread(() -> { pu.print("b", t3); }); t3 = new Thread(() -> { pu.print("c", t1); }); t1.start(); t2.start(); t3.start(); LockSupport.unpark(t1); }
Last updated on