javaee论坛

普通会员

225648

帖子

337

回复

351

积分

楼主
发表于 2019-11-07 13:17:03 | 查看: 333 | 回复: 0

目录一、需求分析二、代码实现1.继承Thread类的方式解决继承Thread类,线程不共享数据的问题2.使用实现Runnable接口的方式3.解决卖票过程中的线程安全问题

一、需求分析

利用多线程的思想模拟三个窗口售票员卖30张票的功能

火车站要售票,我们模拟火车站的售票过程。假设正值春运时期,西安到兰州的动车票只有30张(西安火车站窗口只能卖30张票)。我们采用线程对象来模拟火车站的售票窗口,实现多个窗口同时卖火车票,采用Runnable接口子类来模拟票数。

二、代码实现1.继承Thread类的方式classTicketWindowextendsThread{//车票数量privateintticket=30;@Overridepublicvoidrun(){while(true){if(ticket>0){System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket--+"张票,"+"剩余"+ticket+"张票");}else{System.out.println(Thread.currentThread().getName()+"余票不足,停止售票!");break;}}}}publicclassSellTicketTest1{publicstaticvoidmain(String[]args){TicketWindowtw1=newTicketWindow();TicketWindowtw2=newTicketWindow();TicketWindowtw3=newTicketWindow();tw1.setName("窗口1");tw2.setName("窗口2");tw3.setName("窗口3");tw1.start();tw2.start();tw3.start();}}

代码执行结果:

窗口1卖出了第30张票,剩余29张票窗口1卖出了第29张票,剩余28张票窗口1卖出了第28张票,剩余27张票窗口1卖出了第27张票,剩余26张票窗口1卖出了第26张票,剩余25张票窗口1卖出了第25张票,剩余24张票窗口1卖出了第24张票,剩余23张票窗口1卖出了第23张票,剩余22张票窗口1卖出了第22张票,剩余21张票窗口1卖出了第21张票,剩余20张票窗口1卖出了第20张票,剩余19张票窗口1卖出了第19张票,剩余18张票窗口1卖出了第18张票,剩余17张票窗口1卖出了第17张票,剩余16张票窗口1卖出了第16张票,剩余15张票窗口1卖出了第15张票,剩余14张票窗口1卖出了第14张票,剩余13张票窗口1卖出了第13张票,剩余12张票窗口1卖出了第12张票,剩余11张票窗口1卖出了第11张票,剩余10张票窗口1卖出了第10张票,剩余9张票窗口1卖出了第9张票,剩余8张票窗口1卖出了第8张票,剩余7张票窗口1卖出了第7张票,剩余6张票窗口1卖出了第6张票,剩余5张票窗口1卖出了第5张票,剩余4张票窗口1卖出了第4张票,剩余3张票窗口1卖出了第3张票,剩余2张票窗口1卖出了第2张票,剩余1张票窗口1卖出了第1张票,剩余0张票窗口1余票不足,停止售票!窗口3卖出了第30张票,剩余29张票窗口3卖出了第29张票,剩余28张票窗口3卖出了第28张票,剩余27张票窗口3卖出了第27张票,剩余26张票窗口3卖出了第26张票,剩余25张票窗口3卖出了第25张票,剩余24张票窗口3卖出了第24张票,剩余23张票窗口3卖出了第23张票,剩余22张票窗口3卖出了第22张票,剩余21张票窗口3卖出了第21张票,剩余20张票窗口3卖出了第20张票,剩余19张票窗口3卖出了第19张票,剩余18张票窗口3卖出了第18张票,剩余17张票窗口3卖出了第17张票,剩余16张票窗口3卖出了第16张票,剩余15张票窗口3卖出了第15张票,剩余14张票窗口3卖出了第14张票,剩余13张票窗口3卖出了第13张票,剩余12张票窗口3卖出了第12张票,剩余11张票窗口3卖出了第11张票,剩余10张票窗口3卖出了第10张票,剩余9张票窗口3卖出了第9张票,剩余8张票窗口3卖出了第8张票,剩余7张票窗口3卖出了第7张票,剩余6张票窗口3卖出了第6张票,剩余5张票窗口3卖出了第5张票,剩余4张票窗口3卖出了第4张票,剩余3张票窗口3卖出了第3张票,剩余2张票窗口3卖出了第2张票,剩余1张票窗口3卖出了第1张票,剩余0张票窗口3余票不足,停止售票!窗口2卖出了第30张票,剩余29张票窗口2卖出了第29张票,剩余28张票窗口2卖出了第28张票,剩余27张票窗口2卖出了第27张票,剩余26张票窗口2卖出了第26张票,剩余25张票窗口2卖出了第25张票,剩余24张票窗口2卖出了第24张票,剩余23张票窗口2卖出了第23张票,剩余22张票窗口2卖出了第22张票,剩余21张票窗口2卖出了第21张票,剩余20张票窗口2卖出了第20张票,剩余19张票窗口2卖出了第19张票,剩余18张票窗口2卖出了第18张票,剩余17张票窗口2卖出了第17张票,剩余16张票窗口2卖出了第16张票,剩余15张票窗口2卖出了第15张票,剩余14张票窗口2卖出了第14张票,剩余13张票窗口2卖出了第13张票,剩余12张票窗口2卖出了第12张票,剩余11张票窗口2卖出了第11张票,剩余10张票窗口2卖出了第10张票,剩余9张票窗口2卖出了第9张票,剩余8张票窗口2卖出了第8张票,剩余7张票窗口2卖出了第7张票,剩余6张票窗口2卖出了第6张票,剩余5张票窗口2卖出了第5张票,剩余4张票窗口2卖出了第4张票,剩余3张票窗口2卖出了第3张票,剩余2张票窗口2卖出了第2张票,剩余1张票窗口2卖出了第1张票,剩余0张票窗口2余票不足,停止售票!

通过代码执行结果可以看出,在运行的过程中出现了错误。因为窗口总共有30张票,可是窗口1、窗口2、窗口3各自卖出了30张票,等于卖出去90张票,说明车票没有共享。

解决继承Thread类,线程不共享数据的问题

为了解决车票没有共享的问题,我们需要加static关键字,将车票声明为静态变量,让三个售票窗口线程共享车票。

classTicketWindowextendsThread{//车票数量privatestaticintticket=30;@Overridepublicvoidrun(){while(true){if(ticket>0){System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket--+"张票,"+"剩余"+ticket+"张票");}else{System.out.println(Thread.currentThread().getName()+"余票不足,停止售票!");break;}}}}publicclassSellTicketTest1{publicstaticvoidmain(String[]args){TicketWindowtw1=newTicketWindow();TicketWindowtw2=newTicketWindow();TicketWindowtw3=newTicketWindow();tw1.setName("窗口1");tw2.setName("窗口2");tw3.setName("窗口3");tw1.start();tw2.start();tw3.start();}}

代码执行结果:

窗口1卖出了第30张票,剩余29张票窗口1卖出了第28张票,剩余27张票窗口1卖出了第27张票,剩余26张票窗口1卖出了第26张票,剩余25张票窗口1卖出了第25张票,剩余24张票窗口2卖出了第29张票,剩余28张票窗口2卖出了第22张票,剩余21张票窗口2卖出了第21张票,剩余20张票窗口2卖出了第20张票,剩余19张票窗口2卖出了第19张票,剩余18张票窗口3卖出了第23张票,剩余22张票窗口3卖出了第17张票,剩余16张票窗口3卖出了第16张票,剩余15张票窗口3卖出了第15张票,剩余14张票窗口3卖出了第14张票,剩余13张票窗口3卖出了第13张票,剩余12张票窗口3卖出了第12张票,剩余11张票窗口3卖出了第11张票,剩余10张票窗口3卖出了第10张票,剩余9张票窗口3卖出了第9张票,剩余8张票窗口3卖出了第8张票,剩余7张票窗口3卖出了第7张票,剩余6张票窗口3卖出了第6张票,剩余5张票窗口3卖出了第5张票,剩余4张票窗口3卖出了第4张票,剩余3张票窗口3卖出了第3张票,剩余2张票窗口3卖出了第2张票,剩余1张票窗口3卖出了第1张票,剩余0张票窗口3余票不足,停止售票!窗口1卖出了第24张票,剩余23张票窗口1余票不足,停止售票!窗口2卖出了第18张票,剩余17张票窗口2余票不足,停止售票!2.使用实现Runnable接口的方式

一个类通过继承Thread来实现多线程的话,则不适合多个线程共享资源,而通过实现Runnable就可以做到这一点。

classTicketWindow2implementsRunnable{privateintticket=30;@Overridepublicvoidrun(){while(true){if(ticket>0){System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket--+"张票,"+"剩余"+ticket+"张票");}else{System.out.println(Thread.currentThread().getName()+"余票不足,停止售票!");break;}}}}publicclassSellTicketTest2{publicstaticvoidmain(String[]args){TicketWindow2tw=newTicketWindow2();for(inti=1;i<4;i++){Threadt=newThread(tw,"窗口"+i);t.start();}}}

为了节省篇幅,这儿不再展示测试结果!

第二种方式不需要加static关键字把车票声明为静态的,也实现了数据共享的问题。实现Runnable接口的方式创建多线程,只产生了一个TicketWindow2对象,一个对象里边有一个属性,这样三个线程同时在操作一个属性,运行同一个run方法。在实际开发中,推荐使用第二种方式。

以上两种方式存在线程安全问题:

打印车票时,会出现重票和错票。造成这个问题的原因是一个线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在安全问题。

如何解决:

当线程1在操作ticket的时候,其他线程不能参与进来。直到线程1操作完ticket时,其他线程才可以开始操作ticket。这种情况即使线程1出现了阻塞,也不能被改变。

在Java中,我们通过同步机制,来解决线程的安全问题。

3.解决卖票过程中的线程安全问题

在这篇博客中,使用了同步方法的方式解决了线程安全问题。

代码实现:

classTicketWindow3implementsRunnable{privatestaticintticket=30;@Overridepublicvoidrun(){while(true){synchronized(this){if(ticket>0){try{//线程出现阻塞Thread.sleep(100);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket--+"张票,"+"剩余"+ticket+"张票");}else{System.out.println(Thread.currentThread().getName()+"余票不足,停止售票!");break;}}}}}publicclassSellTicketTest3{publicstaticvoidmain(String[]args){TicketWindow3tw=newTicketWindow3();for(inti=1;i<4;i++){Threadt=newThread(tw,"窗口"+i);t.start();}}}

代码执行结果:

窗口1卖出了第30张票,剩余29张票窗口3卖出了第29张票,剩余28张票窗口3卖出了第28张票,剩余27张票窗口2卖出了第27张票,剩余26张票窗口3卖出了第26张票,剩余25张票窗口1卖出了第25张票,剩余24张票窗口1卖出了第24张票,剩余23张票窗口1卖出了第23张票,剩余22张票窗口1卖出了第22张票,剩余21张票窗口1卖出了第21张票,剩余20张票窗口1卖出了第20张票,剩余19张票窗口1卖出了第19张票,剩余18张票窗口3卖出了第18张票,剩余17张票窗口2卖出了第17张票,剩余16张票窗口2卖出了第16张票,剩余15张票窗口2卖出了第15张票,剩余14张票窗口2卖出了第14张票,剩余13张票窗口2卖出了第13张票,剩余12张票窗口2卖出了第12张票,剩余11张票窗口2卖出了第11张票,剩余10张票窗口2卖出了第10张票,剩余9张票窗口2卖出了第9张票,剩余8张票窗口3卖出了第8张票,剩余7张票窗口1卖出了第7张票,剩余6张票窗口3卖出了第6张票,剩余5张票窗口3卖出了第5张票,剩余4张票窗口2卖出了第4张票,剩余3张票窗口3卖出了第3张票,剩余2张票窗口3卖出了第2张票,剩余1张票窗口1卖出了第1张票,剩余0张票窗口1余票不足,停止售票!窗口3余票不足,停止售票!窗口2余票不足,停止售票!

上一篇多线程(二)Java实现生产者和消费者模式


您需要登录后才可以回帖 登录 | 立即注册

触屏版| 电脑版

技术支持 历史网 V2.0 © 2016-2017