使用Java多线程模拟一个售票系统
1.基于继承Thread实现
代码实现:
/**
* 多线程模拟售票,基于Thread
*/
public class TicketSalesByThread {
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
SellTicket sellTicket1 = new SellTicket();
SellTicket sellTicket2 = new SellTicket();
sellTicket.start();
sellTicket1.start();
sellTicket2.start();
}
}
class SellTicket extends Thread {
// 余票
private static int ticketNum = 100;
@Override
public void run() {
while (true) {
if (ticketNum <= 0) {
System.out.println("没有余票!");
break;
}
// 休眠卖票
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("窗口" + Thread.currentThread().getName() +
"售出一张票。" + "剩余票数:" + (--ticketNum));
}
}
}
结果分析:
窗口Thread-1售出一张票。剩余票数:97
...
窗口Thread-2售出一张票。剩余票数:2
窗口Thread-0售出一张票。剩余票数:1
窗口Thread-1售出一张票。剩余票数:0
没有余票!
窗口Thread-2售出一张票。剩余票数:-1
没有余票!
窗口Thread-0售出一张票。剩余票数:-2
没有余票!
可以看到,基于
Thread
的实现,出现了明显的超卖现象!
那么,为什么会发生这种情况呢?
原因分析:
当一个窗口还未执行--ticketNum
操作时,另一个窗口已经进行售票状态,跳过了while
循环的限制
2.基于实现Runnable接口实现
代码实现:
/**
* 多线程模拟售票,基于实现Runnable接口
*/
public class TicketSalesByRunnable {
public static void main(String[] args) {
SellTicket02 sellTicket02 = new SellTicket02();
new Thread(sellTicket02).start();
new Thread(sellTicket02).start();
new Thread(sellTicket02).start();
}
}
class SellTicket02 implements Runnable {
private int ticketNum = 100;
@Override
public void run() {
while (true) {
if (ticketNum <= 0) {
System.out.println("没有余票!");
break;
}
// 休眠卖票
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("窗口" + Thread.currentThread().getName() +
"售出一张票。" + "剩余票数:" + (--ticketNum));
}
}
}
结果分析:
窗口Thread-0售出一张票。剩余票数:99
...
窗口Thread-0售出一张票。剩余票数:2
窗口Thread-1售出一张票。剩余票数:1
窗口Thread-2售出一张票。剩余票数:0
没有余票!
窗口Thread-0售出一张票。剩余票数:-1
没有余票!
窗口Thread-1售出一张票。剩余票数:-2
没有余票!
仍然会有超卖现象发生!
3.通知线程退出
有时候,我们需要不同的线程可以相互控制对方的运行
可以采用通知线程中止来解决此类问题,比如我们简单实现一个demo,实现主线程通知中止其他线程的效果:
/**
* 通知线程中止
*/
public class ThreadExit {
public static void main(String[] args) throws InterruptedException {
TTT ttt = new TTT();
ttt.start();
Thread.sleep(5000);
// 主线程通知其他线程退出
ttt.setLoop(false);
}
}
class TTT extends Thread {
// 控制变量
private boolean loop = true;
@Override
public void run() {
while (loop) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("TTT线程正在运行...");
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
TTT线程会运行5秒,随后由主线程通知退出
4.解决超卖问题,线程同步机制
在多线程编程中,一些敏感数据不允许被多个线程同时访问,此时就可以使用同步访问技术,保证数据在同一时刻只能有一个线程访问,以保证数据的完整性
synchronized
线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作
上成品代码:
/**
* 使用同步机制解决售票问题的超卖现象
*/
public class TicketSalesResult {
public static void main(String[] args) {
SellTicketRes sellTicketRes = new SellTicketRes();
new Thread(sellTicketRes).start();
new Thread(sellTicketRes).start();
new Thread(sellTicketRes).start();
}
}
class SellTicketRes implements Runnable {
private int ticketNum = 100;
private boolean loop = true;
/**
* 使用synchronized实现线程同步,解决超卖
*/
public synchronized void Sell() {
if (ticketNum <= 0) {
System.out.println("没有余票!");
loop = false;
return;
}
// 休眠卖票
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("窗口" + Thread.currentThread().getName() +
"售出一张票。" + "剩余票数:" + (--ticketNum));
}
@Override
public void run() {
while (true) {
if (loop == false) {
break;
}
Sell();
}
}
}
结果分析:(完美解决超卖问题)
窗口Thread-0售出一张票。剩余票数:99
...
窗口Thread-2售出一张票。剩余票数:1
窗口Thread-2售出一张票。剩余票数:0
没有余票!
没有余票!
没有余票!