Bootstrap

Java多线程实现售票系统

  • 学习多线程比较经典的案例就是实现售票系统了。

  • 我们先来看看需求:铁道部发布了一个售票任务,要求销售100张票,要求有5个窗口来进行销售,效果如下:

      窗口001正在销售第100张票
      窗口001正在销售第99张票
      窗口002正在销售第98张票
      . . .
      窗口05正在销售第1张票
      票已经销售完毕
    
  • 对需求进行分析

      一共有1000张票,5个窗口同步执行;所以需要用到多线程技术,其中车票数应该被5个窗口共享,不然容易出现出售同一张票的问题。
    
  •   下面用两种实现线程的方式来完成需求:
    
一、通过继承Thread类
package com.hym.Threaded;

public class SaleSysTest {
	public static void main(String[] args) {
	
		//创建5个线程,传入 线程名: [001]、[002]...并开启
		for(int i=1;i<6;i++){
			new SaleThread("[00"+i+"]").start();
		}

	}
}

class SaleThread extends Thread {

	//车票数被共享,所以定义为static,数量为100.
	private static int ticket = 100;
	//通过锁来保证线程不会重复访问.
	private static Object obj = new Object();
	
	//有参构造传入线程名
	public SaleThread(String name) {
		super(name);
	}
	public SaleThread() {

	}

	//重写run()方法,将售票动作放入其中;
	@Override
	public void run() {
		while (true) {
			//在锁外使用sleep( );以便于更好的解决复现问题
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//使用synchronized需要锁统一资源,不然没效果,所以将obj初始化为static静态。
			//但让也可以使用字符串 synchronized("lock");lock随便输入
			synchronized (obj) {
			
				//还有余票---出票
				if (ticket > 0) {
					System.out.println(Thread.currentThread().getName() + "号窗口第"
							+ ticket + "票正在出票...");
					ticket--;
				} else {
					//车票售完
					System.out.println("车票已售完,下次请趁早...");
					//不能使用break或者return,后面进行解释。
					System.exit(0);
				}
			}
		}
	}
}
  • 执行结果:----太长了只截了末尾部分大伙凑合凑合
    在这里插入图片描述
二、通过实现Runnable接口
  • 与第一种方式的区别不大
package com.hym.Threaded;

public class Test {

    public static void main(String[] args) {
        //得到对象
        SaleSys ss = new SaleSys();
        //把对象放入线程中
        for(int i=1;i<6;i++){
        	new Thread(ss,"[00"+i+"]").start();
        }
    }
}

class SaleSys implements  Runnable {     
    //定义票的总数
    private int ticket = 100;
    //定义一个线程同步对象
    private Object obj = new Object();
    
	@Override
    public void run() {
         
        while(true){
 			 try {
                Thread.sleep(50);
              } catch (InterruptedException e) {
                 e.printStackTrace();
              }
            //同步锁
            synchronized(obj){
                if(ticket > 0){
                    System.out.println(Thread.currentThread().getName()+" 售出第   "+ticket +"  张票");
                    ticket--;
                }else{
                    System.out.println("票已售完,请下次再来!");
                    System.exit(0);
                }
            }
        }
         
    }
}
  • 执行结果
    在这里插入图片描述

  • 最后解释一下为什么使用System.exit(0)退出,而不是Break,也不是Return。
    system.exit(0):

      是方法调用,在程序的任何地方都会显式表明JVM进程要退出系统了,返回值为0,可以通过%errorlevel%来取得。  
    

return:

   它是一个关键字,表明返回调用当前方法的方法中。 如果是在main()方法中,可以起到退出虚拟机就的作用,但是如果是存在多线程的话return并不能保证JVM退出.,因为要所有的线程都结束才行。
   当不在main()方法中时,System.exit(0)直接终止程序,就算后面有代码也不执行了,而return则返回至调用该方法的地方,如果后面还有代码则继续执行,Break想必就不用说了吧。
  • 在此用return,break的话就会出现 " 下次请趁早+5 " 的情况。当然如果你需要出现每个窗口都提示票售空的情况可以使用break;主要看需求。
;