首先看题目描述:

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

窗口3:卖出第100张票

窗口4:卖出第99张票

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

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

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

看代码:

  

 package com.lesson.thread;

 public class MyThread {

     public static void main(String[] args) {
Ticket sell1 = new Ticket();
Ticket sell2 = new Ticket();
Ticket sell3 = new Ticket();
Ticket sell4 = new Ticket();
sell1.setName("窗口1");
sell2.setName("窗口2");
sell3.setName("窗口3");
sell4.setName("窗口4");
sell1.start();
sell2.start();
sell3.start();
sell4.start();
}
}
class Ticket extends Thread {
private static int tickets = 100;//这里设置成static,目的是让每个线程共享这个变量。以免出现重复打印的现象。
@Override
public void run() {
while(true) {
if(tickets <= 0) {
break;
}
29     System.out.println(getName()+":买出第"+tickets--+"张票。");//卖出一张减一张票
}
} }

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

为什么?

看下面的代码:

package thread;
public class Mythread { public static void main(String[] args) {
Ticket sell1 = new Ticket();
Ticket sell2 = new Ticket();
Ticket sell3 = new Ticket();
Ticket sell4 = new Ticket();
sell1.setName("窗口1");
sell2.setName("窗口2");
sell3.setName("窗口3");
sell4.setName("窗口4");
sell1.start();
sell2.start();
sell3.start();
sell4.start();
}
}
class Ticket extends Thread {
private static int tickets = 100;
@Override
public void run() {
while(true) {
if(tickets <= 0) {
break;
}
try {
Thread.sleep(10); //让进来的线程睡10ms;线程1,2,3,4都睡在这
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+":买出了第"+tickets--+"张票。");
}
}
}

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

窗口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这是唯一的。

package thread;
public class Mythread { public static void main(String[] args) {
Ticket sell1 = new Ticket();
Ticket sell2 = new Ticket();
Ticket sell3 = new Ticket();
Ticket sell4 = new Ticket();
sell1.setName("窗口1");
sell2.setName("窗口2");
sell3.setName("窗口3");
sell4.setName("窗口4");
sell1.start();
sell2.start();
sell3.start();
sell4.start();
}
}
class Ticket extends Thread {
private static int tickets = 100;
@Override
public void run() {
while(true) {
synchronized (Ticket.class) {//同步代码块
if(tickets <= 0) {
break;
}
try {
Thread.sleep(100); //每100ms卖出一张
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+":买出了第"+tickets--+"张票。");
}
}
}
}

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. 使用 WebClient 來存取 GET,POST,PUT,DELETE,PATCH 網路資源

    WebClient 基本資訊 提供通用方法使用 WebRequest 類別傳送及接收 URI (支援 http:, https:, ftp:,和 file: ) 的資源 Namespace:Syste ...

  2. pycharm下运行unittest的问题

    环境: 系统:window7 64 软件:pycharm 版本:2016.3.2 问题描述: 使用unittest类的时候出现问题,问题截图如下 Pycharm 2016.2执行单元测试遇到如下问题: ...

  3. Vue-cli3 WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB)

    在vue.config.js里 添加 configureWebpack : { performance: { hints:'warning', //入口起点的最大体积 整数类型(以字节为单位) max ...

  4. Gson使用

    Gson提供了fromJson()方法来实现从Json相关对象到Java实体的方法. 在日常应用中,我们一般都会碰到两种情况,转成单一实体对象和转换成对象列表或者其他结构. 先来看第一种: 比如jso ...

  5. localStorage、sessionStorage用法总结

    1.作用 1.1 共同点:       都是用来存储客户端临时信息的对象.       均只能存储字符串类型的对象(虽然规范中可以存储其他原生类型的对象,但是目前为止没有浏览器对其进行实现). 1.2 ...

  6. Rabbitmq的五种模式和案例

    消息生产者p将消息放入队列 消费者监听队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列删除 (缺点:消息可能没有被消费者正确处理,已经消失了,无法恢复) 应用场景:聊天室 1.引入依赖 &l ...

  7. (转)nginx日志配置指令详解

    这篇文章主要介绍了nginx日志配置指令详解,nginx有一个非常灵活的日志记录模式,每个级别的配置可以有各自独立的访问日志,需要的朋友可以参考下日志对于统计排错来说非常有利的.本文总结了nginx日 ...

  8. 给你的移动网站加点料:推荐下载App,如果本地安装则直接打开本地App(Android/IOS)

    纵观现在每家移动网站,打开首页的时候,都有各种各样的形式来提示你下载自身的移动App(Android/IOS),这是做移动客户端产品的一个很好地引流的手段.当然各家引流下载的交互和视觉各不相同,有的是 ...

  9. Js正则Replace方法

    JS正则的创建有两种方式: new RegExp() 和 直接字面量. //使用RegExp对象创建 var regObj = new RegExp("(^\s+)|(\s+$)" ...

  10. voip通话分析(含语音质量)

    SipAnalysis.exe使用python开发,通过抓取网卡通信包进行质量分析:1) 分析VOIP通话的发起方.挂机方及对应时间点2) 分析通话使用的媒体信息(方向.载荷.切换时间)3) 分析通话 ...