背景
有三个线程,每个线程分别循环输出A、B、C,各线程循环10次,要求输出结果是ABCABCABC这样的
代码
java
@Data
public class PrintThread extends Thread {
private String string; // 输出的字符串
private int order; // 输出的顺序
private static Object lock; // 静态锁对象
private static volatile int index = 0; // 共享的索引变量
public PrintThread(String string, int order, Object lock) {
this.string = string;
this.order = order;
this.lock = lock;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (lock) { // 使用锁对象进行同步
while (index % 3 != order) { // 判断是否轮到当前线程输出
try {
lock.wait(); // 如果不是轮到当前线程输出,则释放锁并等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(10); // 模拟输出过程的耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
index++; // 修改索引变量,表示下一个线程可以输出了
System.out.println(string); // 输出字符串
lock.notifyAll(); // 唤醒其他等待的线程
}
}
}
public static void main(String[] args) {
try {
Object lock = new Object(); // 创建锁对象
PrintThread threadA = new PrintThread("A", 0, lock); // 创建线程A
PrintThread threadB = new PrintThread("B", 1, lock); // 创建线程B
PrintThread threadC = new PrintThread("C", 2, lock); // 创建线程C
threadA.start(); // 启动线程A
threadB.start(); // 启动线程B
threadC.start(); // 启动线程C
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后
实际会有这样的场景吗?下面举几个例子
1、假设在一个食堂,有很多人在排队打饭,每个人需要完成以下步骤:先拿餐具,然后拿菜,再拿饭,最后付钱。
2、多线程下载器。当我们下载一个大文件时,可以使用多个线程同时从不同的服务器上下载文件的不同部分,然后将这些部分合并成一个完整的文件。通过多个线程交替遍历不同的服务器,可以提高下载速度,加快文件的下载过程。
3、医院的门诊、机场的登机口、超市的收银台等等。
扩展
除了wait+notifyAll,还有其他的实现方式
-
使用
CountDownLatch
:CountDownLatch
是一个同步辅助类,可以用于控制一个或多个线程等待其他线程完成操作。它通过一个计数器来实现,线程调用await()
方法等待计数器变为0,而其他线程调用countDown()
方法来减少计数器的值。当计数器变为0时,等待的线程将被唤醒。 -
使用
CyclicBarrier
:CyclicBarrier
也是一个同步辅助类,可以用于多个线程之间的同步。它和CountDownLatch
类似,都是通过计数器来实现线程的等待和唤醒。不同之处在于,CyclicBarrier
的计数器可以重复使用,当计数器减为0时,所有等待的线程都会被唤醒,并且计数器会被重置为初始值。 -
使用
Semaphore
:Semaphore
是一个计数信号量,可以用来控制同时访问某个资源的线程个数。它维护了一个许可证的计数器,线程可以通过acquire()
方法获取许可证,如果计数器大于0,线程可以继续执行;如果计数器为0,线程将被阻塞。线程在使用完资源后,需要调用release()
方法释放许可证,使得其他线程可以继续访问资源。 -
使用
Lock
和Condition
:Lock
是一个可重入的互斥锁,可以用来替代synchronized
关键字实现线程的同步。Condition
是与Lock
相关联的条件对象,可以用来实现线程的等待和唤醒。线程可以通过调用await()
方法等待条件满足,而其他线程可以通过调用signal()
或signalAll()
方法来唤醒等待的线程。