同步模式之顺序控制

固定顺序

wait notify实现

@Slf4j(topic = "c.Test25") public class Test25 { static final Object lock = new Object(); // 表示 t2 是否运行过 static boolean t2runned = false; public static void main(String[] args) { Thread t1 = new Thread(() -> { synchronized (lock) { 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(); } }
08:35:33.087 c.Test25 [t2] - 2 08:35:33.089 c.Test25 [t1] - 1

Park Unpark 版

可以看到,实现上很麻烦:
  1. 首先,需要保证先 wait 再 notify,否则 wait 线程永远得不到唤醒。因此使用了『运行标记』来判断该不该wait
  1. 第二,如果有些干扰线程错误地 notify 了 wait 线程,条件不满足时还要重新等待,使用了 while 循环来解决此问题
  1. 最后,唤醒对象上的 wait 线程需要使用 notifyAll,因为『同步对象』上的等待线程可能不止一个
可以使用 LockSupport 类的 park 和 unpark 来简化上面的题目:
@Slf4j(topic = "c.Test26") public class Test26 { 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 SyncWaitNotify { private int flag; private int loopNumber; public SyncWaitNotify(int flag, int loopNumber) { this.flag = flag; this.loopNumber = loopNumber; } public void print(int waitFlag, int nextFlag, String str) { for (int i = 0; i < loopNumber; i++) { synchronized (this) { while (this.flag != waitFlag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.print(str); flag = nextFlag; this.notifyAll(); } } } }
 
public class TestOrder { public static void main(String[] args) { test1(); } private static void test1() { 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\n"); }).start(); } }
abc abc abc abc abc

Lock 条件变量版

 
class SyncLock extends ReentrantLock { Condition waitSet = this.newCondition(); private int flag; private int loopNumber; public SyncLock(int flag, int loopNumber) { this.flag = flag; this.loopNumber = loopNumber; } public void print(int waitFlag, int nextFlag, String str) { for (int i = 0; i < loopNumber; i++) { this.lock(); try { while (this.flag != waitFlag) { try { waitSet.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.print(str); this.flag = nextFlag; waitSet.signalAll(); } finally { this.unlock(); } } } }
 
public class TestOrder { public static void main(String[] args) { test2(); } private static void test2() { SyncLock syncLock = new SyncLock(1, 5); new Thread(() -> { syncLock.print(1, 2, "a"); }).start(); new Thread(() -> { syncLock.print(2, 3, "b"); }).start(); new Thread(() -> { syncLock.print(3, 1, "c\n"); }).start(); } }
该实现没有考虑 a,b,c 线程都就绪再开始

Park Unpark 版

class SyncPark { private int loopNumber; private Thread[] threads; public SyncPark(int loopNumber) { this.loopNumber = loopNumber; } public void setThreads(Thread... threads) { this.threads = threads; } public void print(String str) { for (int i = 0; i < loopNumber; i++) { LockSupport.park(); System.out.print(str); LockSupport.unpark(nextThread()); } } private Thread nextThread() { Thread current = Thread.currentThread(); int index = 0; for (int i = 0; i < threads.length; i++) { if(threads[i] == current) { index = i; break; } } if(index < threads.length - 1) { return threads[index+1]; } else { return threads[0]; } } public void start() { for (Thread thread : threads) { thread.start(); } LockSupport.unpark(threads[0]); } }
 
public class TestOrder { public static void main(String[] args) { test3(); } private static void test3() { SyncPark syncPark = new SyncPark(3); Thread t1 = new Thread(() -> { syncPark.print("a"); }); Thread t2 = new Thread(() -> { syncPark.print("b"); }); Thread t3 = new Thread(() -> { syncPark.print("c\n"); }); syncPark.setThreads(t1, t2, t3); syncPark.start(); } }