多线程实现不连续售票

实现一个由A、B、C三个窗口同时销售100张票的系统

1.  创建3个线程分别代表三个窗口(10分)

2.  三个窗口同时开始售票,要求打印出每个窗口打印的售票情况(10分)

3.  每个窗口不得连续售票。当剩余票为0时,线程停止运行(10分)


创建一个票类作为线程操作的资源

public class Ticket{
	int tickets;
	boolean flag = true;
	// 传入需要卖的票的总票数
	public Ticket(int tickets) {
		this.tickets = tickets;
	}

	public synchronized void saleTick() throws Exception {
		while(flag){
			if(tickets > 0){
				//每200毫秒卖一张票
				Thread.sleep(200);
				System.out.println(Thread.currentThread().getName()+
						"  卖出了"+(tickets--)+"号票");
				//在进入等待之前,先唤醒其他线程
				this.notifyAll();
				//卖完一张票后开始等待被唤醒
				this.wait();
			}else{
				flag = false;
				//结束循环前,唤醒所有在等待的线程
				this.notifyAll();
			}
		}
	}
}

创建销售线程: 

public class SaleWin implements Runnable{
	private Ticket ticket;
	
	public SaleWin(Ticket ticket) {
		this.ticket = ticket;
	}
	@Override
	public void run() {
		while(ticket.flag){
			try {
				ticket.saleTick();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

创建测试类:

public class Test {
	public static void main(String[] args) {
		Ticket ticket = new Ticket(100);
		SaleWin sw = new SaleWin(ticket);
		new Thread(sw,"窗口A").start();
		new Thread(sw,"窗口B").start();
		new Thread(sw,"窗口C").start();
	}
}


<think>我们使用多线程编程来实现长途汽车售票系统。关键点在于:多个售票窗口(线程)共享车票资源,需要确保车票售出重复且按顺序出票(即售票序号连续)。我们可以使用同步机制(synchronized)来保证同一时间只有一个线程在操作票。同时,我们使用一个共享变量来表示当前剩余票和当前售出的票号(或下一张要售出的票号)。设计思路:1.定义一个车票类(或直接使用一个共享变量)来记录当前待售的车票序号。初始票号为1,最大票号为m(35)。2.创建n个(2个)线程,每个线程代表一个售票窗口。3.每个线程的任务是循环售票,直到所有车票售完。在每次售票时,先获取锁,然后检查是否还有票,如果有则售出一张(打印售票信息,并将票号加1),然后释放锁。4.使用同步机制确保同一时间只有一个线程在执行售票操作。有两种常见实现方式:a.实现Runnable接口,共享同一个Runnable实例(这样多个线程共享同一个票源)。b.继承Thread类,但使用一个静态变量来记录票(这样多个线程共享静态变量)。根据用户要求,我们使用两个窗口(线程)出售35张票。这里我们采用实现Runnable接口的方式,因为这样更自然地共享同一个票源。代码实现:步骤:1.创建一个Ticket类实现Runnable接口。2.在Ticket类中定义私有变量tickets,表示当前待售的票号(从1开始),以及总票MAX(35)。3.在run方法中,使用循环来售票,直到票售完(tickets>MAX)。4.在循环中,使用synchronized同步块(或同步方法)来保证每次只有一个线程操作票。注意,我们同步的对象可以是任意一个共享对象,但通常使用this(在多个线程共享同一个Runnable实例时有效)或者一个显式的锁对象。这里我们使用this。5.在同步块中,再次检查是否还有票(避免在等待锁的过程中票已售完),然后售票(打印售票信息),并将票号加1。6.创建两个线程,共享同一个Ticket实例,然后启动线程。注意:为了避免线程在售票过程中出现竞争条件,同步是必须的。代码示例:```javapublic classTicket implementsRunnable {privateint ticket=1;//当前待售的票号,从1开始privatefinal intMAX =35; //总票@Overridepublic voidrun(){while (true){//使用synchronized同步块,锁对象是当前实例(因为多个线程共享同一个Runnable实例,所以this是共享的)synchronized(this) {if(ticket >MAX){break;//票已售完,退出循环}//模拟售票时间try {Thread.sleep(100);//休眠100毫秒模拟售票过程}catch (InterruptedExceptione){e.printStackTrace();}//售票System.out.println(Thread.currentThread().getName() +"售出第" +ticket +"张票");ticket++;}//释放锁后,可以让其他线程有机会获取锁}}publicstatic voidmain(String[] args) {TicketticketRunnable =new Ticket();//创建两个线程,模拟两个售票窗口Thread t1 =new Thread(ticketRunnable, "窗口1");Thread t2 =new Thread(ticketRunnable, "窗口2");t1.start();t2.start();} }```说明:1.使用synchronized(this)同步块,确保同一时间只有一个线程可以进入该代码块。2.在同步块内,我们检查当前票号是否超过最大值(35),如果超过则退出循环。3.然后模拟售票(休眠100毫秒),打印售票信息,并将票号加1。4.两个线程共享同一个Ticket实例,因此共享ticket变量。这样,可以保证车票按顺序出票(因为每次售票操作都是原子的,且票号递增),并且会重复售卖(因为每次售票前都会检查票号,且票号递增操作在同步块内完成)。注意:这里使用synchronized同步块,锁对象是当前实例(this),因为两个线程共享同一个ticketRunnable对象,所以锁是有效的。另一种方式是使用同步方法,但这里我们使用同步块。运行结果示例(部分):窗口1售出第1张票窗口2售出第2张票窗口1售出第3张票...窗口2售出第35张票由于线程调度的确定性,两个窗口售票的顺序可能交替,但票号一定是连续的,且会重复。引用说明:我们参考了多线程同步售票的基本模型[^1][^2][^3],确保共享资源的安全访问。§§相关问题§§1.如果要求三个售票窗口同时售票,代码需要如何修改? 2.除了使用synchronized同步块,还有哪些方法可以实现线程安全售票? 3.在售票系统中,如何避免死锁?</think>### 基于多线程的长途汽车售票系统实现实现多个售票窗口按顺序出售重复车票,核心在于**线程同步**和**共享资源管理**。以下是使用 Java 多线程的解决方案: #### 代码实现 ```java public class BusTicketSystem { public static void main(String[] args) { // 创建共享票源(35张票) TicketPool ticketPool = new TicketPool(35); // 创建两个售票窗口线程 Thread window1 = new Thread(new TicketSeller(ticketPool), "窗口1"); Thread window2 = new Thread(new TicketSeller(ticketPool), "窗口2"); // 启动售票 window1.start(); window2.start(); } } // 票池(共享资源) class TicketPool { private int totalTickets; // 总票 private int currentTicket = 1; // 当前待售票号 public TicketPool(int total) { this.totalTickets = total; } // 同步售票方法 public synchronized int sellTicket() { if (currentTicket > totalTickets) { return -1; // 票已售完 } int soldTicket = currentTicket; currentTicket++; return soldTicket; } } // 售票窗口 class TicketSeller implements Runnable { private final TicketPool ticketPool; public TicketSeller(TicketPool pool) { this.ticketPool = pool; } @Override public void run() { while (true) { int ticketNum = ticketPool.sellTicket(); if (ticketNum == -1) { System.out.println(Thread.currentThread().getName() + ":票已售罄"); break; } // 模拟售票耗时 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 售出第 " + ticketNum + " 号票"); } } } ``` #### 关键设计说明 1. **线程安全保证** - 使用 `synchronized` 修饰 `sellTicket()` 方法,确保同一时间只有一个线程能操作票号 - 共享资源 `TicketPool` 被所有售票窗口线程共用 2. **票号管理** - `currentTicket` 记录下一个待售票号 - 每次售票后执行 `currentTicket++` 保证票号连续 - 票号检查与修改在原子操作中完成 3. **运行结果示例** ``` 窗口1 售出第 1 号票 窗口2 售出第 2 号票 窗口1 售出第 3 号票 ... 窗口2 售出第 35 号票 窗口1:票已售罄 ``` #### 技术要点 1. **同步机制** - 通过 `synchronized` 实现互斥访问 - 避免出现超卖(票号重复)或漏卖问题 2. **资源管理** - 票号作为共享状态集中管理 - 售票逻辑与资源管理分离 3. **扩展性** - 调整 `totalTickets` 可修改总票 - 增加线程量可扩展售票窗口 此方案满足: - 票号连续重复(顺序出票) - 多窗口并发安全 - 票售完自动停止 - 支持动态扩展窗口量[^1][^2]
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值