1. 固定运行顺序
必须先 2 后 1 打印
1.1 wait¬ify
public class WaitAndNotifyall {
//用来同步的对象
public static Object lock = new Object();
// t2 运行标记, 代表 t2 是否执行过
public static boolean t2isprint = false;
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
// 如果 t2 没有执行过
while (!t2isprint) {
try {
// t1 先等一会
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t1");
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
t2isprint = true;
System.out.println("t2");
// 通知 obj 上等待的线程(可能有多个,因此需要用 notifyAll)
lock.notifyAll();
}
}
});
t1.start();
t2.start();
}
}
1.2 park&unpark
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) { }
// 当没有『许可』时,当前线程暂停运行;有『许可』时,用掉这个『许可』,当前线程恢复运行
LockSupport.park();
System.out.println("1");
});
Thread t2 = new Thread(() -> {
System.out.println("2");
// 给线程 t1 发放『许可』(多次连续调用 unpark 只会发放一个『许可』)
LockSupport.unpark(t1);
});
t1.start();
t2.start();
2. 交替输出
线程 1 输出 a 5 次,线程 2 输出 b 5 次,线程 3 输出 c 5 次。现在要求输出 abcabcabcabcabc 怎么实现
2.1 wait¬ify
为了控制是否执行,使用wait
,条件满足的时候就输出,否则进入等待队列等待,所以我们这里使用一个int
类型的等待标记,等待标记为1的时候线程1 执行,为2则线程2执行,为3则线程3执行
为了控制ABC的顺序,让每个线程把自己的事情做完再去唤醒他的下一个线程,所以对于每一个线程都要设置他的下一个线程
输出内容 | 等待标记 | 下一个线程 |
---|---|---|
a | 1 | 2 |
b | 2 | 3 |
c | 3 | 1 |
public class WaitNotifySyn {
public static void main(String[] args) {
WaitNotify waitNotify = new WaitNotify(1,5);
new Thread(new Runnable() {
@Override
public void run() {
waitNotify.print(1, 2, "a");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
waitNotify.print(2, 3, "b");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
waitNotify.print(3, 1, "c");
}
}).start();
}
}
class WaitNotify{
private int flag;//等待标记
private int loopNum;//打印次数
public WaitNotify(int flag, int loopNum) {
this.flag = flag;
this.loopNum = loopNum;
}
public void print(int waitFlag, int nextFlag, String str){
for (int i = 0; i<loopNum; i++){
synchronized (this){
while(waitFlag != flag){//当前要的标记和我的标记一不一样
try {
this.wait();//不一定则等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(str);
flag = nextFlag;//公共标记置为下一个
this.notifyAll();//唤醒所有
}
}
}
}
2.2 使用lock条件变量
我们可以定义三个休息室分别放打印a的线程1,打印b的线程2和打印c的线程3,前一个线程结束后唤醒他后一个的休息室就行了
public class AwaitSignalTest {
public static void main(String[] args) throws InterruptedException {
AwaitAndSignal awaitAndSignal = new AwaitAndSignal(5);
Condition conditionA = awaitAndSignal.newCondition();
Condition conditionB = awaitAndSignal.newCondition();
Condition conditionC = awaitAndSignal.newCondition();
new Thread(()->{
awaitAndSignal.print("a",conditionA ,conditionB);
}).start();
new Thread(()->{
awaitAndSignal.print("b",conditionB ,conditionC);
}).start();
new Thread(()->{
awaitAndSignal.print("c",conditionC ,conditionA);
}).start();
//等他们都在休息室后还需要推他一把 不然都在休息室
TimeUnit.SECONDS.sleep(2);
awaitAndSignal.lock();
try{
conditionA.signalAll();
}finally {
awaitAndSignal.unlock();
}
}
}
class AwaitAndSignal extends ReentrantLock {
private int loopnum;
public AwaitAndSignal(int loopnum) {
this.loopnum = loopnum;
}
public void print(String str, Condition current, Condition next){
for (int i = 0; i < loopnum; i++) {
this.lock();
try {
//先进入休息室
current.await();
System.out.println(str);
next.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
this.unlock();
}
}
}
}
2.3 使用park&unpark
针对每一个线程我们呢只需要知道下一个需要唤醒的线程就行
public class ParkTest {
static Thread t1;
static Thread t2;
static Thread t3;
public static void main(String[] args) throws InterruptedException {
ParkUnPark parkUnPark = new ParkUnPark();
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();
TimeUnit.SECONDS.sleep(1);
LockSupport.unpark(t1);
}
}
class ParkUnPark{
public void print(String str, Thread next){
for (int i = 0; i < 5; i++) {
LockSupport.park();
System.out.println(str);
LockSupport.unpark(next);
}
}
}