1、固定运行顺序
比如,必须先2后1打印
1.1、wait notify 版本
static Object obj = new Object();
static boolean t2runed = false;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (obj) {
while(!t2runed) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(1);
}
}, "t1");
Thread t2 = new Thread(() -> {
System.out.println(2);
synchronized (obj) {
t2runed = true;
obj.notifyAll();
}
},"t2");
t1.start();
t2.start();
}
输出
2
1
1.2、park unpark 版本
可以看到,上面的实现很麻烦:
- 首先,需要保证先wait在notify,否则wait线程永远得不到唤醒,因此使用了【运行标记】来判断该不该wait
- 第二,如果有些干扰线程错误地notify了wait线程,条件不满足时还需要 重新等待,使用while循环来解决此问题
- 最后,唤醒对象上的wait线程需要使用notifyAll,因为【同步对象】上等待的线程可能不止一个
可以使用LockSupport类的park和unpark来简化上面的题目;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.park();
System.out.println(1);
}, "t1");
Thread t2 = new Thread(() -> {
System.out.println(2);
LockSupport.unpark(t1);
}, "t2");
t1.start();
t2.start();
}
park和unpark方法比较灵活,他们谁先调用,谁后调动无所谓,并且是以线程为单位进行【暂停】和【恢复】,不需要【同步对象】和【运行标记】
2、交替输出
线程1 输出a 5次,线程2 输出b 5次,线程3 输出 c 5次,现在要求输出abcabcabcabcabc怎么实现
2.1、wait notify版本
public class StepControlV3 {
public static void main(String[] args) {
SyncWaitNotify syncWaitNotify = new SyncWaitNotify(1, 5);
Thread t1 = new Thread(() -> {
syncWaitNotify.print(1, 2, "a");
}, "t1");
Thread t2 = new Thread(() -> {
syncWaitNotify.print(2, 3, "b");
}, "t2");
Thread t3 = new Thread(() -> {
syncWaitNotify.print(3, 1, "c");
}, "t3");
t1.start();
t2.start();
t3.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println();
SyncWaitNotify2 syncWaitNotify2 = new SyncWaitNotify2(5, 3);
Thread t4 = new Thread(() -> {
syncWaitNotify2.print(1, "a");
}, "t4");
Thread t5 = new Thread(() -> {
syncWaitNotify2.print(2, "b");
}, "t5");
Thread t6 = new Thread(() -> {
syncWaitNotify2.print(0, "c");
}, "t6");
t4.start();
t5.start();
t6.start();
}
}
class SyncWaitNotify {
int loopTime = 0;
int flg = 0;
public SyncWaitNotify(int flg, int loopTime) {
this.flg = flg;
this.loopTime = loopTime;
}
public void print(int waitFlg, int nextFlg, String str) {
for (int i = 0; i < loopTime; i++) {
synchronized (this) {
while (flg != waitFlg) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(str);
flg = nextFlg;
this.notifyAll();
}
}
}
}
class SyncWaitNotify2 {
int loopTime = 0;
int cnt = 0;
int threadCnt = 0;
public SyncWaitNotify2(int loopTime, int threadCnt) {
this.loopTime = loopTime;
this.threadCnt = threadCnt;
}
public void print(int waitFlg, String str) {
for (int i = 0; i < loopTime; i++) {
synchronized (this) {
while ((cnt + 1) % 3 != waitFlg) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(str);
cnt++;
this.notifyAll();
}
}
}
}
2.2、Lock条件变量版本
public class StepControlV4 {
public static void main(String[] args) {
AwaitSignal awaitSignal = new AwaitSignal(5);
Condition aCondition = awaitSignal.newCondition();
Condition bCondition = awaitSignal.newCondition();
Condition cCondition = awaitSignal.newCondition();
Thread t1 = new Thread(() -> {
awaitSignal.print("a", aCondition, bCondition);
}, "t1");
Thread t2 = new Thread(() -> {
awaitSignal.print("b", bCondition, cCondition);
}, "t2");
Thread t3 = new Thread(() -> {
awaitSignal.print("c", cCondition, aCondition);
}, "t3");
t1.start();
t2.start();
t3.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
awaitSignal.start(aCondition);
}
}
@Slf4j(topic = "c.AwaitSignal")
class AwaitSignal extends ReentrantLock {
int loopTime;
public AwaitSignal(int loopTime) {
this.loopTime = loopTime;
}
public void start(Condition first) {
this.lock();
try {
log.debug("start");
first.signal();
} finally {
this.unlock();
}
}
public void print(String str, Condition cur, Condition next) {
for (int i = 0; i < loopTime; i++) {
this.lock();
try {
cur.await();
log.debug(str);
next.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
this.unlock();
}
}
}
}
2.3、park unpark 版本
@Slf4j(topic = "c.StepControlV5")
public class StepControlV5 {
public static void main(String[] args) {
SyncPark syncPark = new SyncPark(5);
Thread t1 = new Thread(() -> {
syncPark.print("a");
}, "t1");
Thread t2 = new Thread(() -> {
syncPark.print("b");
}, "t2");
Thread t3 = new Thread(() -> {
syncPark.print("c");
}, "t3");
syncPark.setThreads(new Thread[]{t1, t2, t3});
syncPark.start();
}
}
@Slf4j(topic = "c.SyncPark")
class SyncPark {
int loopTime;
List<Thread> threads;
public SyncPark(int loopTime) {
this.loopTime = loopTime;
}
public void setThreads(Thread[] threads) {
this.threads = Arrays.asList(threads);
}
public void start() {
for (Thread thread : threads) {
thread.start();
}
LockSupport.unpark(threads.get(0));
}
public void print(String str) {
for (int i = 0; i < loopTime; i++) {
LockSupport.park();
log.debug(str);
LockSupport.unpark(nextThread());
}
}
public Thread nextThread() {
Thread currentThread = Thread.currentThread();
int index = threads.indexOf(currentThread);
return index == (threads.size() - 1) ? threads.get(0) : threads.get(index + 1);
}
}