Java中,可以通过多线程模拟叫号看病系统,使用一个共享队列来存储排队的病人号码,一个加号线程定期向队列中添加号码,一个或多个叫号线程定期从队列中取出号码并模拟就诊
Java中实现线程叫号看病的模拟,可以通过多线程技术来模拟病人挂号、排队等待以及医生叫号看病的过程,下面将详细介绍如何实现这一过程,包括关键类的设计、线程的使用以及同步机制的应用。
系统设计
整个叫号系统可以分为以下几个主要部分:

- 病人类(Patient):表示每个病人,包含病人的编号、姓名等信息。
- 挂号类(Registration):负责生成病人编号,并将病人加入候诊队列。
- 候诊区类(WaitingRoom):使用队列数据结构管理等待看病的病人。
- 医生类(Doctor):负责从候诊区中取出病人号码进行治疗。
- 叫号机类(CallingMachine):模拟叫号的过程,按照一定的规则叫号。
- 看病过程类(TreatmentProcess):模拟医生看病的过程,包括处理特需号和普通号的逻辑。
关键类的设计
1 病人类(Patient)
public class Patient {
private int number;
private String name;
private boolean isSpecial; // 是否为特需号
public Patient(int number, String name, boolean isSpecial) {
this.number = number;
this.name = name;
this.isSpecial = isSpecial;
}
public int getNumber() {
return number;
}
public String getName() {
return name;
}
public boolean isSpecial() {
return isSpecial;
}
}
2 挂号类(Registration)
import java.util.concurrent.atomic.AtomicInteger;
public class Registration {
private AtomicInteger atomicInteger;
public Registration() {
this.atomicInteger = new AtomicInteger(1);
}
public int generateNumber() {
return atomicInteger.getAndIncrement();
}
}
3 候诊区类(WaitingRoom)
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
public class WaitingRoom {
private Queue<Patient> queue;
public WaitingRoom(int capacity) {
this.queue = new ArrayBlockingQueue<>(capacity);
}
public void addPatient(Patient patient) throws InterruptedException {
queue.add(patient);
System.out.println("Patient " + patient.getName() + " with number " + patient.getNumber() + " is waiting in the waiting room.");
}
public Patient getNextPatient() throws InterruptedException {
Patient patient = queue.poll();
if (patient != null) {
System.out.println("Doctor is calling patient number: " + patient.getNumber() + ", Name: " + patient.getName());
}
return patient;
}
}
4 医生类(Doctor)
public class Doctor implements Runnable {
private WaitingRoom waitingRoom;
private boolean isRunning;
public Doctor(WaitingRoom waitingRoom) {
this.waitingRoom = waitingRoom;
this.isRunning = true;
}
@Override
public void run() {
while (isRunning) {
try {
Patient patient = waitingRoom.getNextPatient();
if (patient != null) {
// 模拟看病时间
int treatmentTime = patient.isSpecial() ? 2000 : 1000; // 特需号看病时间是普通号的2倍
Thread.sleep(treatmentTime);
System.out.println("Finished treating patient number: " + patient.getNumber());
} else {
// 如果没有病人,医生休息一会儿
Thread.sleep(500);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public void stop() {
isRunning = false;
}
}
5 叫号机类(CallingMachine)
public class CallingMachine implements Runnable {
private WaitingRoom waitingRoom;
private Registration registration;
private boolean isRunning;
private int ordinaryCount = 0; // 普通号已叫号数量
private int specialCount = 0; // 特需号已叫号数量
private final int MAX_ORDINARY = 20; // 普通号总数
private final int MAX_SPECIAL = 10; // 特需号总数
public CallingMachine(WaitingRoom waitingRoom, Registration registration) {
this.waitingRoom = waitingRoom;
this.registration = registration;
this.isRunning = true;
}
@Override
public void run() {
while (isRunning) {
try {
// 优先叫特需号,直到特需号叫完或者普通号叫到第10号
if (specialCount < MAX_SPECIAL && (ordinaryCount < 10 || specialCount < MAX_SPECIAL)) {
int number = registration.generateNumber();
boolean isSpecial = number <= MAX_SPECIAL; // 假设前10个号为特需号
Patient patient = new Patient(number, "Patient" + number, isSpecial);
waitingRoom.addPatient(patient);
if (isSpecial) {
specialCount++;
} else {
ordinaryCount++;
}
} else if (ordinaryCount < MAX_ORDINARY) {
// 普通号叫号,但需要先叫完特需号
int number = registration.generateNumber();
Patient patient = new Patient(number, "Patient" + number, false);
waitingRoom.addPatient(patient);
ordinaryCount++;
} else {
// 所有号都已叫完,停止叫号
isRunning = false;
}
Thread.sleep(500); // 模拟叫号间隔
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public void stop() {
isRunning = false;
}
}
6 医院类(Hospital)
public class Hospital {
private WaitingRoom waitingRoom;
private Registration registration;
private Doctor doctor;
private CallingMachine callingMachine;
private Thread doctorThread;
private Thread callingMachineThread;
public Hospital(int waitingRoomCapacity) {
this.waitingRoom = new WaitingRoom(waitingRoomCapacity);
this.registration = new Registration();
this.doctor = new Doctor(waitingRoom);
this.callingMachine = new CallingMachine(waitingRoom, registration);
}
public void start() {
doctorThread = new Thread(doctor);
callingMachineThread = new Thread(callingMachine);
doctorThread.start();
callingMachineThread.start();
}
public void stop() {
callingMachine.stop();
doctor.stop();
try {
doctorThread.join();
callingMachineThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
Hospital hospital = new Hospital(30); // 候诊区容量为30
hospital.start();
}
}
同步机制与线程安全
在上述实现中,WaitingRoom类使用了ArrayBlockingQueue来保证线程安全,确保多个线程可以安全地添加和获取病人。Registration类使用了AtomicInteger来生成唯一的病人编号,避免了多线程环境下的竞争问题。Doctor和CallingMachine类中的isRunning标志位用于控制线程的停止,确保在需要时可以安全地停止线程。
运行效果与逻辑说明
运行程序后,可以看到挂号系统生成病人号码并加入候诊区,医生叫号后从候诊区取出病人号码,模拟了实际看病的流程,具体逻辑如下:

- 挂号与排队:
CallingMachine线程不断生成病人号码,并根据规则将病人加入候诊区,特需号优先于普通号,直到特需号叫完或者普通号叫到第10号,之后,继续叫普通号,直到所有号叫完。 - 医生叫号与看病:
Doctor线程不断从候诊区中取出病人号码,模拟看病过程,特需号的看病时间是普通号的2倍,医生在没有病人时会休息一会儿,避免空转。 - 线程停止:当所有号都叫完后,
CallingMachine线程停止生成号码,Doctor线程在处理完所有病人后自动停止。Hospital类的stop方法确保所有线程都安全停止。
相关问答FAQs
Q1:如何确保特需号优先于普通号?
A1:在CallingMachine类中,通过判断当前已叫的特需号数量和普通号数量,优先生成特需号,直到特需号叫完或者普通号叫到第10号,这样可以确保特需号优先于普通号。

Q2:如何处理医生在看病过程中的中断?
A2:在Doctor类的run方法中,使用try-catch块捕获InterruptedException异常,并在捕获到异常时调用Thread.currentThread().interrupt()方法,确保线程的中断状态被正确设置,这样,当外部调用stop方法时,医生
