首先看题目描述:

假设有火车票100张,创建4个线程模拟4个售票点,每100ms售出一张,打印出售票过程,格式如下:

窗口3:卖出第100张票

窗口4:卖出第99张票

............

............

简单的思路就是创建一个类,首先肯定要去继承Thread。开启线程,由于是4个窗口,肯定要开启4个线程。然后让每个线程去输出结果,也就是卖出去的票。这里很多人想不到如何让4个线程不打印重复的票。(比如4个线程都卖出去了第100张票,这显然是不合理的)。

看代码:

  

  1. package com.lesson.thread;
  2.  
  3. public class MyThread {
  4.  
  5. public static void main(String[] args) {
  6. Ticket sell1 = new Ticket();
  7. Ticket sell2 = new Ticket();
  8. Ticket sell3 = new Ticket();
  9. Ticket sell4 = new Ticket();
  10. sell1.setName("窗口1");
  11. sell2.setName("窗口2");
  12. sell3.setName("窗口3");
  13. sell4.setName("窗口4");
  14. sell1.start();
  15. sell2.start();
  16. sell3.start();
  17. sell4.start();
  18. }
  19. }
  20. class Ticket extends Thread {
  21. private static int tickets = 100;//这里设置成static,目的是让每个线程共享这个变量。以免出现重复打印的现象。
  22. @Override
  23. public void run() {
  24. while(true) {
  25. if(tickets <= 0) {
  26. break;
  27. }
  28. 29     System.out.println(getName()+":买出第"+tickets--+"张票。");//卖出一张减一张票
  29. }
  30. }
  31.  
  32. }

可能你已经看到了你想要的结果了。但是,还没完。目前代码写到这里是有问题的!!!

为什么?

看下面的代码:

  1. package thread;
  2. public class Mythread {
  3.  
  4. public static void main(String[] args) {
  5. Ticket sell1 = new Ticket();
  6. Ticket sell2 = new Ticket();
  7. Ticket sell3 = new Ticket();
  8. Ticket sell4 = new Ticket();
  9. sell1.setName("窗口1");
  10. sell2.setName("窗口2");
  11. sell3.setName("窗口3");
  12. sell4.setName("窗口4");
  13. sell1.start();
  14. sell2.start();
  15. sell3.start();
  16. sell4.start();
  17. }
  18. }
  19. class Ticket extends Thread {
  20. private static int tickets = 100;
  21. @Override
  22. public void run() {
  23. while(true) {
  24. if(tickets <= 0) {
  25. break;
  26. }
  27. try {
  28. Thread.sleep(10); //让进来的线程睡10ms;线程1,2,3,4都睡在这
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. System.out.println(getName()+":买出了第"+tickets--+"张票。");
  33. }
  34. }
  35. }

与开始的代码不同在于让进来的线程睡一会。可以看到如下运行结果:

窗口2:买出了第0张票。
窗口3:买出了第-1张票。
窗口1:买出了第-2张票。

这里编者调出来了出问题的地方。可以看到怎么会第0,-1,-2张票???

做出解释:

其实在这里让线程睡一会就是为了演示这里有很多行代码要执行。假设票已经卖到第1张了,也就是tickets=1,然后第一条线程进来判断 tickets <= 0 ?不成立,然后线程1要睡 10ms,紧接着,线程2进来,这时 tickets 还是为1,因为线程1在睡,tickets 没有减 。然后线程2判断 tickets <= 0 ? 还是不成立,线程2又开始睡。同样,线程3,线程4都睡了。 这时的tickets 还是等于1的。然后线程1先醒过来,开始输出结果,tickets 减了1。可是这是其他的线程还是经过while里面的判断语句进来了的,只是睡了。所以当其他线程醒过来的时候,还是会打印出结果的。也就出现了上面的问题。

解决方法:

 多线程并发改变同一变量,为了解决,采用同步代码块synchronized。里面加任意的对象,但是不能加this,因为这里创建了四个线程,每一个线程都有自己的对象,所以是四个不同的对象,没有用。所以这里不能用this,必须锁在同一个对象里才行。而Thickets.class这是唯一的。

  1. package thread;
  2. public class Mythread {
  3.  
  4. public static void main(String[] args) {
  5. Ticket sell1 = new Ticket();
  6. Ticket sell2 = new Ticket();
  7. Ticket sell3 = new Ticket();
  8. Ticket sell4 = new Ticket();
  9. sell1.setName("窗口1");
  10. sell2.setName("窗口2");
  11. sell3.setName("窗口3");
  12. sell4.setName("窗口4");
  13. sell1.start();
  14. sell2.start();
  15. sell3.start();
  16. sell4.start();
  17. }
  18. }
  19. class Ticket extends Thread {
  20. private static int tickets = 100;
  21. @Override
  22. public void run() {
  23. while(true) {
  24. synchronized (Ticket.class) {//同步代码块
  25. if(tickets <= 0) {
  26. break;
  27. }
  28. try {
  29. Thread.sleep(100); //每100ms卖出一张
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. System.out.println(getName()+":买出了第"+tickets--+"张票。");
  34. }
  35. }
  36. }
  37. }

JAVA学习之路(多线程)---模拟售票(细解)的更多相关文章

  1. java学习之路--多线程实现的方法

    1 继承Thread类 继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Th ...

  2. java学习之路之javaSE基础1

    <h2>java学习之路之javaSE基础1</h2> <div> ###01.01_计算机基础知识(计算机概述)(了解)* A:什么是计算机?计算机在生活中的应用 ...

  3. 新篇章之我的java学习之路下

    昨天写下了人生的第一篇博客,今天接着写我的java学习之路有关开发及框架的学习过程. 想要学好java语言,只学习一些java的基本语法对实际开发中的用处还是不大的,所以我们还要掌握一些有关javaW ...

  4. java学习之路之javaSE基础2

    java学习之路之javaSE基础2 所有的代码都是引用他人写的. 1.逻辑运算符 //&,|,^,! //int x = 10; //5 < x < 15 //x > 5 ...

  5. Java学习之路(转)

    Java学习之路(书籍推荐)   一.基础类 1.<Thinking in java>(阅读2遍),入门第一位是建立正确的概念 2.<Core Java>这本书更贴近实践,更多 ...

  6. JAVA学习之路与大家分享

    这是我四年以前总结的JAVA学习之路,希望对初学者或想从事JAVA开发的人有帮助. 本人是软件工程专业出身,先后学过C.C++.JAVA.VB.delphi等等开发语言以及网络相关管理技术.哎,好久不 ...

  7. Java 学习之路 -- day00

    Java 学习之路 -- day00 Typora 快捷键操作 标题:#+空格 2. *斜体* 3. **加粗** 4. **斜体加粗*** 5. ~~删除线~~ 6. > 引用 7. ···分 ...

  8. Java学习手记2——多线程

    一.线程的概念 CPU执行程序,就好比一个人在干事情一样,同一个时间你只能做一件事情,但是这样的效率实在是太低了,在你用电脑的时候,听歌就不能浏览网页,看电影就不能下载视频,你想想是不是很蛋疼. 所以 ...

  9. (转)如何学习Java技术?谈Java学习之路

    51CTO编者注:这篇文章已经是有数年“网龄”的老文,不过在今天看来仍然经典.如何学习Java?本篇文章可以说也是面对编程初学者的一篇指导文章,其中对于如何学习Java的步骤的介绍,很多也适用于开发领 ...

随机推荐

  1. 案例1-合并2个不同文件夹中的csv文件到另外一个目录,相同的文件名进行数据合并,不同的文件名直接移到新文件夹

    发现在ubuntu和centos中有些命令还不一样,比如<<<可在centos中使用,但是ubuntu中不行 csv文件名以及格式如下 3669_20180121.csv 总笔数,2 ...

  2. 关于:maven项目中pom.xml文件添加依赖无法自动搜索的问题

    用的是eclipse 1.Window------>Show View------->Maven Repositories(这个选项一般不直接显示,而在others里面) 2.操作完毕后会 ...

  3. LINQ -2015-04-27

    LINQ--language-integrated-query 1.它和sql语言区别呢? SQL语言常用在ralational-database中,而LINQ对内存数据,数据库,xml文件等多种形式 ...

  4. HTML-★★★★★表单★★★★★

    表单 <form id="" name="" method="post/get" action="负责处理的服务端" ...

  5. css 命名规划

    命名规范 前言中略微描述了 CSS 怎么使用:下面介绍一下 CSS 的一些代码规范: CSS 命名一般采用小写英文单词或组合命名,单词与单词间以"-"分割:英文单词不缩写,除非一看 ...

  6. 由import javax.persistence.*;引用引发问题的思考(SpringBoot)

    在学习SpringBoot的 JPA时候增加一个实体类的时候发现import包的时候一直报错. 对比一下,发现自己的项目里面是少了 spring-boot-starter-data-jpa.jar包的 ...

  7. python 获取文件路径

    一种是获取当前你正在操作文件的路径 一种是获取你执行文件的路径(比如你在你调用的包里面更改了,执行的时候就不会找你的包的路径,而是你执行文件的路径)

  8. Angular material mat-icon 资源参考_Communication

    ul,li>ol { margin-bottom: 0 } dt { font-weight: 700 } dd { margin: 0 1.5em 1.5em } img { height: ...

  9. final学习

    类加载过程 1.装载:查找和导入Class文件 2.链接:其中解析步骤是可以选择的 (a)检查:检查载入的class文件数据的正确性 (b)准备:给类的静态变量分配存储空间 (c)解析:将符号引用转成 ...

  10. easyUI----纵向合并单元格

    使用Easyui-DataGrid过程用.做统计/报表等时经常会使用到合并单元格,原生态Easyui-DataGrid没有合并单元格的属性或方法. 解决方案: 代码一 onLoadSuccess: f ...