Java多线程(卖票案例)

1.卖票案例需求分析

某天某个景区售票,门票票只有100张,景区有三个售票窗口,同时售票,共享票源(100张)
通过多线程的方式实现三个售票窗口同时售票

  1. 首先要明确票源只能有一个
  2. 其次需要创建三个Thread的对象去执行卖票的方法,卖完票后要将剩余的票数返回给统一的票源;
2.代码实现

1.票源和多线程执行入口代码

public class Ticket implements Runnable {

    private int tickets = 100;  //票源

    TicketOffice ticketOffice = new TicketOffice();

    @Override
    public void run() {
   		while(true){
        	this.tickets = ticketOffice.sellTicket(tickets);  //sellTicket由synchronized修饰
        //返回的tickets重新传给成员变量this.tickets以保证票源的一致
		}
    }
}

2.售票规则代码

public class TicketOffice {

    
    public synchronized int sellTicket(int tickets) {

        if (tickets > 0) {

            try {
                Thread.sleep(0);  //模拟CPU忙碌的停顿
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Thread th = Thread.currentThread();
            System.out.println(th.getName() + " 线程数为:" + Thread.activeCount() + "  出售第:" + tickets--);
        }
        return tickets;
    }
}

在案例中我把售票窗口代码写道了另一个类中,并设置为了静态方法,可以直接通过类名来调用
3.测试类代码

public class ThreadDemo {

    public static void main(String[] args) {

        Ticket ticket = new Ticket();  //票源固定100张

        Thread thread0 = new Thread(ticket);  //创建新线程
        thread0.start();

        Thread thread1 = new Thread(ticket);
        thread1.start();

        Thread thread2 = new Thread(ticket);
        thread2.start();
    }
2.1 结果分析

为了分析结果的方便,我们将票源修改为5张,观察结果是否出错
在这里插入图片描述
!!! 显然输出结果有问题,我们设置的票源总共有5张,而此时三个窗口总和加起来卖了15张!

2.2 问题分析

通过代码分析发现是synchronized锁的不够全面出现的,在售票规则代码块中售票方法被synchronized修饰,方法内部的共享数据tickets被锁住,一个线程为执行完方法时,另一线程不允许进入。==方法内部的“门”锁好了,但是忽略的run()中调用方法需要传形参的这道“门”!==由于ticketOffice.sellTicket(tickets);方法运行时间较长,在传入形参的时候有可能三个线程都进行了ticketOffice.sellTicket(tickets);中形参的赋值任务,从而导致会输出三个线程都从第5张票开始贩卖。因此我们需要在外部也就是this.tickets = ticketOffice.sellTicket(tickets);的外部也加上synchronized代码块,从而使得共享成员不会错误赋值。

3.代码修改

只需要在外部也就是this.tickets = ticketOffice.sellTicket(tickets);的外部也加上synchronized代码块即可;

public class Ticket implements Runnable {

    private int tickets = 100;

    TicketOffice ticketOffice = new TicketOffice();

    @Override
    public void run() {
    	while(true){
        	synchronized (this) {

            	this.tickets = ticketOffice.sellTicket(tickets);  //sellTicket由synchronized修饰

        	}
		}
    }
}

这样运行出的结果就是正确的了

小结:大家在其他地方应该也看到过类似的案例,他们一般是把售票规则代码与run()方法放在同一个类中,这样会避免许多错误,这样就显得我的改动可能是画蛇添足,不过还是能从这样的修改中明白synchronized的使用位置,和文章中这种错误出现的原因所在;所以个人认为代码不能按着老师的写法来,学习代码我们需要有自己的思路,自己的方法,遇到问题需要用自己解决问题的方式。

问题:在运行过程中我发现,在没有synchronized修饰线程操作所共享数据时,输出结果中每个线程都有售出票的显示,而加入synchronized操作时结果显示虽然有3个(输出中显示5个,是因为包含了main和JVM虚拟机运行所使用的线程)线程在运行,但只有Thread-0在售票,很疑惑…

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值