一、固定运行顺序
必须先 2 后 1 打印
1.1 wait notify 版
// 用来同步的对象
static Object obj = new Object();
// t2 运行标记, 代表 t2 是否执行过
static boolean t2runed = false;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (obj) {
// 如果 t2 没有执行过
while (!t2runed) {
try {
// t1 先等一会
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println(1);
});
Thread t2 = new Thread(() -> {
System.out.println(2);
synchronized (obj) {
// 修改运行标记
t2runed = true;
// 通知 obj 上等待的线程(可能有多个,因此需要用 notifyAll)
obj.notifyAll();
}
});
t1.start();
t2.start();
}
1.2 Park Unpark 版
Thread t1 = new Thread(() -> {
LockSupport.park();
System.out.println(1);
});
Thread t2 = new Thread(() -> {
System.out.println(2);
LockSupport.unpark(t1);
});
t1.start();
t2.start();
二、交替输出
线程 1 输出 a 5 次,线程 2 输出 b 5 次,线程 3 输出 c 5 次。现在要求输出 abcabcabcabcabc 怎么实现
2.1 wait notify 版
@Slf4j
public class test {
public static void main(String[] args) {
WaitNotify waitNotify = new WaitNotify(1, 5); //初始让1线程先执行,共循环5次
new Thread(() -> waitNotify.print(1, 2, "a")).start();
new Thread(() -> waitNotify.print(2, 3, "b")).start();
new Thread(() -> waitNotify.print(3, 1, "c")).start();
}
}
class WaitNotify {
private int flag; //当前允许哪个线程执行
private int loopNumber; //循环次数
public WaitNotify(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) {
throw new RuntimeException(e);
}
}
this.flag = nextFlag;
System.out.print(str + " ");
this.notifyAll();
}
}
}
}
2.2 Lock 条件变量版
@Slf4j
public class test {
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(1000L);
awaitSignal.lock();
try {
System.out.println("开始");
a.signal(); //唤醒和进入休息室的前提是获取锁 (被唤醒后,重新竞争锁,获取到后又可执行唤醒next线程操作)
} finally {
awaitSignal.unlock();
}
}
}
class AwaitSignal extends ReentrantLock {
private int loopNumber;
public AwaitSignal(int loopNumber) {
this.loopNumber = loopNumber;
}
public void print(String str, Condition current, Condition next) {
for(int i = 0; i < loopNumber; i ++) {
lock();
try {
current.await(); //当前线程进入current休息室等待
System.out.print(str + " ");
next.signal(); //唤醒下一个线程
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
unlock();
}
}
}
}
2.3 Park Unpark 版
@Slf4j
public class test {
static Thread t1, t2, t3;
public static void main(String[] args) throws InterruptedException {
ParkUnpark pk = new ParkUnpark(5);
t1 = new Thread(() -> pk.print("a", t2));
t2 = new Thread(() -> pk.print("b", t3));
t3 = new Thread(() -> pk.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);
}
}
}