java 线程交替执行_多线程交替运行

目标

三个线程分别打印a,b,c,需要保证打印顺序是abc

思路

需要保证线程的执行顺序,本质还是线程之间通信,线程之间的通信有以下几种方式

wait()/notify()/notifyAll()

使用wait/notify,需要通过临时变量来控制,当前是否打印,在线程打印完毕后更新临时变量的值,并唤醒所有等待中的线程,等待中的线程唤醒后,通过比较新的临时变量值,是否是自己想要的值来决定是继续等待,还是执行打印。

比方说a,b,c,对应的打印条件分别是flag=1,2,3,那么当flag=1的时候,a执行打印,然后flag+1,调用notifyAll()唤醒其他线程;此时flag已经是2,线程b唤醒后,比较flag 发现是2,执行打印逻辑,线程c被唤醒后,发现flag是2,但是自己的打印条件是flag=3,继续等待。通过while循环来控制等待-比较-更新的逻辑,代码如下

class WaitNotifyPrinter {

/*** 循环次数*/

private int loopNum;

private int flag;

public WaitNotifyPrinter(int loopNum, int flag) {

this.loopNum = loopNum;

this.flag = flag;

}

/*** 打印消息** @param message 要打印的内容* @param current 当前flag* @param next 要唤醒的flag*/

public void print(String message, int current, int next) {

synchronized (this) {

for (int i = 0; i < loopNum; i++) {

//判断flag是否和当前一致,一致就打印 while (flag != current) {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.print(message);

//更新flag flag = next;

//更新完需要唤醒正在等待的线程 notifyAll();

}

}

}

}

测试代码

WaitNotifyPrinter p = new WaitNotifyPrinter(5, 1);

new Thread(() -> {

p.print("a", 1, 2);

}).start();

new Thread(() -> {

p.print("b", 2, 3);

}).start();

new Thread(() -> {

p.print("c", 3, 1);

}).start();

LockSupport.park()/unpark()

与wait/notify不同的是,notify唤醒的是所有等待的线程,而LockSupport.park()可以精确到某一个需要唤醒的线程,因此相比wait/notify,无需通过flag来判断线程是否需要打印,只要线程能正确被唤醒,那么即可执行打印逻辑,基于以上代码做小小的改动即可实现

class LockSupportPrinter {

private int loopNum;

public LockSupportPrinter(int loopNum) {

this.loopNum = loopNum;

}

public void print(String message, Thread current, Thread next) {

for (int i = 0; i < loopNum; i++) {

//阻塞当前线程 LockSupport.park(current);

//线程被唤醒后打印 System.out.print(message);

//唤醒下一个线程 LockSupport.unpark(next);

}

}

}

测试代码

LockSupportPrinter lockSupportPrinter = new LockSupportPrinter(5);

a = new Thread(() -> {

lockSupportPrinter.print("a", a, b);

});

b = new Thread(() -> {

lockSupportPrinter.print("b", b, c);

});

c = new Thread(() -> {

lockSupportPrinter.print("c", c, a);

});

a.start();

b.start();

c.start();

//主线程唤醒a,不需要等待a启动后就可以唤醒,因为提前唤醒,下一次LockSupport.park(a)将不被阻塞 LockSupport.unpark(a);

Condition.await()/signal()/signalAll()

Condition是J.U.C并发包下重入锁ReentrantLock的工具类,其功能和wait/notify类似,我们知道wait/notify需要拿到对象的锁才能执行唤醒操作,但是调用notifyAll唤醒的是所有等待中的线程,与Objct.wait()不一样的是,一个ReentrantLock可以拥有多个condition,通过调用指定condition的await()/signal()方法,可以唤醒指定condition中等待的线程。

那么本例子中,三个线程分别打印a,b,c,可以理解为3个condition,打印A的conditionA,打印B的conditionB,打印c的conditionC,在conditionA被唤醒后,说明a打印完了,此时唤醒conditionB,让B线程打印b,然后唤醒conditionC,然后C线程打印c并唤醒conditionA,具体代码见下:

class ConditionPrinter {

private int loopNum;

private ReentrantLock lock;

public ConditionPrinter(int loopNum, ReentrantLock lock) {

this.loopNum = loopNum;

this.lock = lock;

}

public void print(String message, Condition current, Condition next) {

lock.lock();

try {

for (int i = 0; i < loopNum; i++) {

try {

//当前线程等待唤醒 current.await();

//当前线程已经唤醒,执行打印 System.out.print(message);

//唤醒下一个线程 next.signalAll();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

} finally {

lock.unlock();

}

}

}

测试代码

ReentrantLock lock = new ReentrantLock();

Condition conditionA = lock.newCondition();

Condition conditionB = lock.newCondition();

Condition conditionC = lock.newCondition();

ConditionPrinter conditionPrinter = new ConditionPrinter(5, lock);

new Thread(() -> {

conditionPrinter.print("a", conditionA, conditionB);

}).start();

new Thread(() -> {

conditionPrinter.print("b", conditionB, conditionC);

}).start();

new Thread(() -> {

conditionPrinter.print("c", conditionC, conditionA);

}).start();

//等打印a的线程启动后,主线程唤醒该线程 try {

Thread.sleep(100L);

} catch (InterruptedException e) {

e.printStackTrace();

}

//需要拿到锁才能调用condition方法 lock.lock();

try {

conditionA.signal();

} finally {

lock.unlock();

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值