先上代码:
public class Printer {
public static void main(String[] args) {
start();
}
private static int flg = 1;
private static final Object lock = new Object();
//控制打印次数
private static int num = 0;
private static void start() {
Thread t1 = new Thread(() -> {
while (num < 100) {
synchronized (lock) {
if (flg == 1) {
System.out.print("a");
flg = 2;
lock.notifyAll();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}, "t1");
Thread t2 = new Thread(() -> {
while (num < 100) {
synchronized (lock) {
if (flg == 2) {
System.out.print("b");
flg = 3;
lock.notifyAll();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}, "t2");
Thread t3 = new Thread(() -> {
while (num < 100) {
synchronized (lock) {
if (flg == 3) {
System.out.print("c");
num++;
flg = 1;
lock.notifyAll();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}, "t3");
t1.start();
t2.start();
t3.start();
}
}
首先,来一把对象锁 lock,static是在类加载是为了保证锁对象只加载1次,final是为了防止锁引用的对象中间被修改造成并发问题。
然后是start方法,在这里创建3个线程,并启动,直接被main调用,也设为static。
接下来是设置flag信号,表示当前该输出的数字。
那么我们接下来就需要实现Runnable接口了,使用Lambda表达式,首先不考虑计数,我们可以先设为无限循环while(true),然后抢锁,抢到之后,如果发现flag不对,就调wait释放锁,此线程就阻塞在wait那里,直到下次它再被唤醒,才会进入下一轮循环继续抢对象锁。那flag对上了的话,就可以输出了,注意要将flag调成下一个,在最后调下notifyAll唤醒所有monitor等待队列中wait的线程。
设为while(true)是因为循环是不断重试的,有可能抢到锁但本轮并不应该是它输出,所以用了无限循环,这样也会一直交替打印下去,那我们怎么控制器打印次数呢?为此我引入了计数器,打完c之后加一,最后计数器的值小于一个固定值即可!大于就跳出while循环,循环执行结束!注意不能用for循环100次,因为中间的一次循环进入后并不一定flag正确,即并不应该当前线程进行打印!
大家对我这种写法怎么看,欢迎交流呀!