有三个线程,分别只能打印A,B和C,要求按顺序打印ABC,打印10次
输出示例:
ABC
ABC
ABC
ABC
ABC
ABC
ABC
ABC
ABC
ABC
(1)、这种方法并不能达到题目要求,因为无法确认当线程A在打印完一次后释放锁并唤醒其他线程时,它会唤醒线程B和线程C中的哪一个。这就导致,线程的执行顺序可能是ACB,也可能是其他顺序。
public class Demo25 {
public static void main(String[] args) {
final Object lock = new Object();
Thread a = new Thread(() -> {
for (int i = 0; i < 10; i++) {
synchronized (lock) {
System.out.print("A");
lock.notifyAll();
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
});
Thread b = new Thread(() -> {
for (int i = 0; i < 10; i++) {
synchronized (lock) {
System.out.print("B");
lock.notifyAll();
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
});
Thread c = new Thread(() -> {
for (int i = 0; i < 10; i++) {
synchronized (lock) {
System.out.println("C");
lock.notifyAll();
try {
if (i < 9) {
lock.wait();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
});
a.start();
b.start();
c.start();
}
}
(2)、我们刚刚写的代码中的线程因为没有合适的条件判断从而不能够按相应的顺序打印字母,又因为没有终止条件,所以无法正常停止。
现在改进以上错误,重新写一个实现方法:
public class Thread_1{
// 计数器
// 定义一个单独的锁对象
private static volatile int COUNTER = 0;
private static Object lock = new Object();
public static void main(String[] args) {
// 创建三个线程,并指定线程名,每个线程名分别用A,B,C表示
Thread t1 = new Thread(() -> {
// 循环10次
for (int i = 0; i < 10; i++) {
// 执行的代码加锁
synchronized (lock) {
// 每次唤醒后都重新判断是否满足条件
// 每条线程判断的条件不一样,注意线程t1,t2
while (COUNTER % 3 != 0) {
try {
// 不满足输出条件时,主动等待并释放锁
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 满足输出条件,打印线程名,每条线程打印的内容不同
System.out.print(Thread.currentThread().getName());
// 累加计数
COUNTER++;
// 唤醒其他线程
lock.notifyAll();
}
}
}, "A");//线程被命名为 "A"
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
synchronized (lock) {
while (COUNTER % 3 != 1) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
COUNTER++;
lock.notifyAll();
}
}
}, "B");
Thread t3 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
synchronized (lock) {
while (COUNTER % 3 != 2) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//换行打印
System.out.println(Thread.currentThread().getName());
COUNTER++;
lock.notifyAll();
}
}
}, "C");
// 启动线程
t1.start();
t2.start();
t3.start();
}
}
代码 Thread_1 停下来的原因是每个线程都在循环中执行一定次数,并且使用了 lock.wait() 来等待条件满足。当满足条件后,线程会继续执行,否则会等待。
具体来说,代码中的三个线程 t1、t2、t3 会依次执行,它们之间使用了 lock 对象来同步,确保每次只有一个线程可以进入临界区执行。每个线程都有自己的条件检查,例如 t1 检查 COUNTER % 3 是否等于 0,t2 检查 COUNTER % 3 是否等于 1,t3 检查 COUNTER % 3 是否等于 2。这些条件控制了线程的执行顺序,确保了按照 “ABCABCABC” 的顺序输出。
当线程执行完一轮后,会通过 lock.wait() 主动释放锁并等待唤醒,然后其他线程会继续执行,直到满足条件后再次唤醒等待的线程。这种方式保证了线程的有序执行。
总之,这个代码中的线程之间使用了条件判断和等待来控制执行顺序,因此能够正常停止。
3、还有一种实现方法,就是使用三个锁, 分别控制。
public class Demo26 {
private static Object locker1 = new Object();
private static Object locker2 = new Object();
private static Object locker3 = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
synchronized (locker1) {
locker1.wait();
}
System.out.print("A");
synchronized (locker2) {
locker2.notify();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
synchronized (locker2) {
locker2.wait();
}
System.out.print("B");
synchronized (locker3) {
locker3.notify();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t3 = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
synchronized (locker3) {
locker3.wait();
}
//注意,只有C是println
System.out.println("C");
synchronized (locker1) {
locker1.notify();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
t2.start();
t3.start();
Thread.sleep(1000);
// 从线程 t1 启动
synchronized (locker1) {
locker1.notify();
}
}
}
再来看道题吧,和上面的差不多。
有三个线程,线程名称分别为:a,b,c。每个线程打印自己的名称。
需要让他们同时启动,并按 c,b,a 的顺序打印。
public class PrintOrderDemo {
private static final Object lock = new Object();
private static volatile int turn = 0;
public static void main(String[] args) {
Thread threadA = new Thread(() -> {
while (true) {
synchronized (lock) {
while (turn != 2) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("a");
turn = 0;
lock.notifyAll();
}
}
});
Thread threadB = new Thread(() -> {
while (true) {
synchronized (lock) {
while (turn != 1) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("b");
turn = 2;
lock.notifyAll();
}
}
});
Thread threadC = new Thread(() -> {
while (true) {
synchronized (lock) {
while (turn != 0) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("c");
turn = 1;
lock.notifyAll();
}
}
});
threadA.start();
threadB.start();
threadC.start();
}
}