Java 多线程同步实战解析:学生注册配额控制
一、原题完整代码呈现
以下是计算机二级Java考试中关于多线程同步与线程控制的典型题型,主要考察线程类定义、线程启动、循环控制、同步方法及返回值处理,包含5处需要填写的空白(标记为//*********Found********
):
//*********Found********
public class Java_3 ____________________{
static RegistrationAgent agent;
static boolean timetoquit=false;
public static void main(String[] args){
agent = new RegistrationAgent();
Thread[] t= new Thread[3];
for (int i=0; i<3; i++){
t[i] = new Java_3();
//*********Found********
____________________;
}
}
public void run( ){
//*********Found********
while (________________){
boolean r = agent.reg();
if (!r)
timetoquit = true;
try{
Thread.sleep(2);
}catch(Exception e){}
}
}
}
class RegistrationAgent {
private int quota = 0;
public boolean reg(){
synchronized(this){
if( quota < 10){
//*********Found********
_________________;
System.out.print(Thread.currentThread().getName());
System.out.println( " Registered one student, and total " + quota
+" students registered.");
return true;
}
else
//*********Found********
______________;
}
}
}
二、空白处逐一解析与解答
1. 第一处空白:定义线程类(实现Runnable接口)
答案:implements Runnable
//*********Found********
public class Java_3 implements Runnable {
解析: Java_3
类包含run()
方法,这是线程执行的核心逻辑。通过实现Runnable
接口,该类的实例可作为线程的目标对象(Thread
构造方法参数)。相比继承Thread
类,实现接口更灵活(支持多接口实现),因此填写implements Runnable
。
2. 第二处空白:启动线程
答案:t[i].start()
//*********Found********
t[i].start();
解析:
创建线程对象(t[i] = new Thread(new Java_3())
,注:原题中new Java_3()
实际应为new Thread(new Java_3())
,但根据上下文补全启动逻辑)后,需调用start()
方法启动线程。start()
会自动调用run()
方法,而直接调用run()
只是普通方法调用,不会开启新线程,因此填写t[i].start()
。
3. 第三处空白:控制线程循环条件
答案:!timetoquit
//*********Found********
while (!timetoquit){
解析: timetoquit
是静态布尔变量,用于控制所有线程的终止。当timetoquit
为false
时,线程继续执行注册逻辑;当注册配额满(quota >=10
),timetoquit
被设为true
,所有线程退出循环,因此填写!timetoquit
。
4. 第四处空白:累加注册配额
答案:quota++
//*********Found********
quota++;
解析: RegistrationAgent
的reg()
方法用于处理注册逻辑,quota
记录已注册人数。当quota < 10
时,需增加配额计数,quota++
实现自增(等价于quota = quota + 1
),因此填写quota++
。
5. 第五处空白:配额已满时返回结果
答案:return false
//*********Found********
return false;
解析: reg()
方法返回boolean
类型,用于标识注册是否成功。当quota >=10
(配额已满)时,注册失败,需返回false
,触发主线程设置timetoquit = true
,因此填写return false
。
三、完整正确代码
四、程序功能与运行说明
程序功能
该程序模拟多线程环境下的学生注册系统,核心功能包括:
- 创建3个线程并发处理注册请求
- 通过
RegistrationAgent
控制注册配额(最多10人) - 使用同步机制(
synchronized
)保证配额计数的线程安全 - 当配额满时,所有线程终止运行
运行说明
- 程序启动后,创建3个线程并启动,共同调用
agent.reg()
方法 注册过程:
RegistrationAgent
的quota
初始为0,每次成功注册后自增1synchronized(this)
确保多线程对quota
的操作互斥(避免计数错误)- 当
quota
达到10时,reg()
返回false
,触发timetoquit = true
,所有线程退出循环
典型输出(线程名可能不同,顺序取决于调度):
Thread-0 Registered one student, and total 1 students registered. Thread-1 Registered one student, and total 2 students registered. Thread-2 Registered one student, and total 3 students registered. ...(中间省略4-9的输出) Thread-0 Registered one student, and total 10 students registered.
五、核心知识点总结
线程创建方式(实现Runnable接口)
- 接口实现:
public class Java_3 implements Runnable
,需重写run()
方法 - 线程启动:
new Thread(new Java_3()).start()
,将Runnable
实例传入Thread
构造器 - 优势:避免单继承限制,可共享资源(如
agent
实例)
- 接口实现:
同步机制(synchronized)
- 同步块:
synchronized(this)
确保同一时刻只有一个线程执行块内代码 - 作用:保护共享变量(
quota
)的并发访问,避免"丢失更新"等线程安全问题 - 原理:通过对象锁(
this
指代RegistrationAgent
实例)实现线程互斥
- 同步块:
线程控制与通信
- 共享标志:
static boolean timetoquit
作为全局控制变量,协调所有线程的终止 - 循环控制:
while (!timetoquit)
使线程持续执行,直到配额满 - 线程休眠:
Thread.sleep(2)
模拟处理时间,释放CPU资源,让其他线程有机会执行
- 共享标志:
方法返回值与逻辑控制
reg()
方法返回boolean
:true
表示注册成功,false
表示配额已满- 条件判断:根据返回值设置
timetoquit
,实现多线程的协同终止
六、常见错误与应试技巧
常见错误分析
- 第一处空白接口实现错误:写成
extends Thread
(虽可行,但题目更倾向接口实现)或拼写错误(如Runnable
少字母) - 第二处空白线程启动错误:写成
t[i].run()
(仅调用方法,不启动新线程)或start(t[i])
(语法错误) - 第三处空白循环条件错误:写成
timetoquit
(逻辑颠倒,线程无法启动)或quota < 10
(无法共享终止信号) - 第四处空白配额更新错误:写成
quota =+ 1
(语法错误)或遗漏(配额不变,导致无限注册) - 第五处空白返回值错误:写成
return
(缺少返回值)或return true
(配额满仍返回成功)
- 第一处空白接口实现错误:写成
应试技巧
- 线程创建口诀:"实现Runnable,重写run方法,Thread来包装,start启动它"
- 同步机制:"共享资源要同步,synchronized加锁,同一时刻单线程,计数安全不犯错"
- 线程通信:"全局变量作标志,多线程共读写,条件判断控循环,协同终止靠它啦"
- 调试技巧:输出线程名和配额值,观察并发顺序和同步效果
通过本题,我们掌握了多线程的创建与启动、同步机制的应用、线程间通信的基本方式以及共享资源的安全控制。这类并发场景在售票系统、注册系统等实际应用中广泛存在,理解线程同步与协同的原理,对编写高效且安全的并发程序具有重要意义。