上一篇博客 Android-卖票案例static-不推荐此方式,讲解了卖票案例是 private static int ticket = 10;,static静态的这种方式来解决卖票多卖30张的问题,但并不推荐这种方式,因为还有更加合理的方式,那就不得不提到Runnable接口,此篇博客就是使用Runnable来解决卖票多卖30张的问题


需求描述:四个窗口一起卖票,把10张票卖完,不许多卖

先看一个错误的案例:

package android.java.thread06;

/**
* 售票线程
*/
class Booking extends Thread { /**
* 模拟票的总算 10张票
*/
private int ticket = 10; @Override
public void run() {
super.run(); while (ticket > 0) {
System.out.println("名称:" + Thread.currentThread().getName() + "窗口卖出第" + ticket + "张票");
ticket -- ;
}
}
} /**
* 售票案例
*/
public class BookingTest { public static void main(String[] args) { // 实例化线程对象
Thread thread1 = new Booking();
Thread thread2 = new Booking();
Thread thread3 = new Booking();
Thread thread4 = new Booking(); // 开启启动线程
thread1.start(); // 启动第Thread-0窗口 执行卖票任务
thread2.start(); // 启动第Thread-1窗口 执行卖票任务
thread3.start(); // 启动第Thread-2窗口 执行卖票任务
thread4.start(); // 启动第Thread-3窗口 执行卖票任务
} }

日志结果:

名称:Thread-0窗口卖出第10张票
名称:Thread-0窗口卖出第9张票
名称:Thread-0窗口卖出第8张票
名称:Thread-1窗口卖出第10张票
名称:Thread-0窗口卖出第7张票
名称:Thread-0窗口卖出第6张票
名称:Thread-0窗口卖出第5张票
名称:Thread-0窗口卖出第4张票
名称:Thread-0窗口卖出第3张票
名称:Thread-0窗口卖出第2张票
名称:Thread-0窗口卖出第1张票
名称:Thread-1窗口卖出第9张票
名称:Thread-1窗口卖出第8张票
名称:Thread-1窗口卖出第7张票
名称:Thread-1窗口卖出第6张票
名称:Thread-1窗口卖出第5张票
名称:Thread-1窗口卖出第4张票
名称:Thread-1窗口卖出第3张票
名称:Thread-1窗口卖出第2张票
名称:Thread-2窗口卖出第10张票
名称:Thread-2窗口卖出第9张票
名称:Thread-1窗口卖出第1张票
名称:Thread-2窗口卖出第8张票
名称:Thread-2窗口卖出第7张票
名称:Thread-3窗口卖出第10张票
名称:Thread-3窗口卖出第9张票
名称:Thread-2窗口卖出第6张票
名称:Thread-3窗口卖出第8张票
名称:Thread-3窗口卖出第7张票
名称:Thread-2窗口卖出第5张票
名称:Thread-3窗口卖出第6张票
名称:Thread-2窗口卖出第4张票
名称:Thread-3窗口卖出第5张票
名称:Thread-2窗口卖出第3张票
名称:Thread-3窗口卖出第4张票
名称:Thread-2窗口卖出第2张票
名称:Thread-2窗口卖出第1张票
名称:Thread-3窗口卖出第3张票
名称:Thread-3窗口卖出第2张票
名称:Thread-3窗口卖出第1张票

从日志结果来看,没有实现需求,反而多卖了30张,例如:本来一节车厢坐10人,结果一节车厢卖了40张票,这是非常严重的错误

为什么会这样呢,看内存图就明白了:

由于 Thread-0线程有自己的ticket变量

   Thread-1线程有自己的ticket变量

   Thread-2线程有自己的ticket变量

   Thread-3线程有自己的ticket变量

    所以才会造成40张票


在main方法中,thread1.start();/thread2.start();/thread3.start();/thread4.start();  四个线程去启动一个Runnable实现类Booking,ticket变量就是只有一个了

package android.java.thread07;

/**
* 售票线程
*/
class Booking implements Runnable { /**
* 模拟票的总算 10张票
*/
private int ticket = 10; @Override
public void run() { while (ticket > 0) { if (ticket > 0) {
System.out.println("名称:" + Thread.currentThread().getName() + "窗口卖出第" + ticket + "张票");
ticket--;
}
} }
} /**
* 售票案例
*/
public class BookingTest { public static void main(String[] args) { /**
* 定义Runnable实现类Booking,此实现类Booking不是线程,此实现类Booking给四个Thread去执行的
*/
Runnable booking = new Booking(); // 实例化线程对象
Thread thread1 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread2 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread3 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread4 = new Thread(booking); // 此实现类Booking给Thread去执行的 // 开启启动线程
thread1.start(); // 启动第Thread-0窗口 执行卖票任务
thread2.start(); // 启动第Thread-1窗口 执行卖票任务
thread3.start(); // 启动第Thread-2窗口 执行卖票任务
thread4.start(); // 启动第Thread-3窗口 执行卖票任务
} }

结果出现相同的票,这是CPU非常非常快速切换随机性造成的:

结果只出现了 Thread-0  和 Thread-1 那是因为 Thread-0 已经把ticket-- 到 0了,所以无法其他的线程对象无法进入while循环,也就无法看到打印


解决CPU快速切换四个线程,导致重复卖票的问题,加同步synchronized

package android.java.thread07;

/**
* 售票线程
*/
class Booking implements Runnable { /**
* 模拟票的总算 10张票
*/
private int ticket = 10; @Override
public void run() { while (ticket > 0) { /**
* 加入同步标识,进行拦截,例如:我在ticket-- =9 的时候 其他线程不准ticket-- =9,这样就解决了重复--值的问题
*/
synchronized (Booking.class) {
/**
* 必须在同步里面再次判断 ticket > 0 , 因为CUP对四个线程切换太快 有可能ticket=-1 或 ticket=-2 ... ,所以必须再次判断
*/
if (ticket > 0) {
System.out.println("名称:" + Thread.currentThread().getName() + "窗口卖出第" + ticket + "张票");
ticket--;
}
}
} }
} /**
* 售票案例
*/
public class BookingTest { public static void main(String[] args) { /**
* 定义Runnable实现类Booking,此实现类Booking不是线程,此实现类Booking给四个Thread去执行的
*/
Runnable booking = new Booking(); // 实例化线程对象
Thread thread1 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread2 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread3 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread4 = new Thread(booking); // 此实现类Booking给Thread去执行的 // 开启启动线程
thread1.start(); // 启动第Thread-0窗口 执行卖票任务
thread2.start(); // 启动第Thread-1窗口 执行卖票任务
thread3.start(); // 启动第Thread-2窗口 执行卖票任务
thread4.start(); // 启动第Thread-3窗口 执行卖票任务
} }

执行结果:已经满足需求

Android-Java卖票案例-推荐此方式Runnable的更多相关文章

  1. JUC 并发编程--01,线程,进程,经典卖票案例, juc的写法

    进程: 就是一个程序, 里面包含多个线程, 比如一个QQ程序 线程: 进程中最小的调度单元, 比如 QQ中的自动保存功能 并发: 多个线程操作同一资源, 抢夺一个cpu的执行片段, 快速交替 并行: ...

  2. Java基础学习笔记: 多线程,线程池,同步锁(Lock,synchronized )(Thread类,ExecutorService ,Future类)(卖火车票案例)

    多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念.进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线 ...

  3. java多线程实现卖票小程序

    package shb.java.demo; /** * 多线程测试卖票小程序. * @Package:shb.java.demo * @Description: * @author shaobn * ...

  4. Android-卖票案例static-不推荐此方式

    需求描述:四个窗口一起卖票,把10张票卖完,不许多卖 先看一个错误的案例: package android.java.thread06; /** * 售票线程 */ class Booking ext ...

  5. java 多线程之卖票两种方式

    1.通过extends Thread /* 需求:简单的卖票,多个窗口同时买票 (共用资源) 创建线程的第二种方式:实现Runnable接口 步骤: 1,定义类实现Runnable接口 2,覆盖/重写 ...

  6. Java线程同步(synchronized)——卖票问题

    卖票问题通常被用来举例说明线程同步问题,在Java中,采用关键字synchronized关键字来解决线程同步的问题. Java任意类型的对象都有一个标志位,该标志位具有0,1两种状态,其开始状态为1, ...

  7. java学习多线程之卖票示例

    这一节我们来说一个示例就是卖票示例: 需求: 我们现在有100张票,然后分四个窗口来卖,直到卖完为止. 思路: 1.先定一个一个票类,描述票的属性,还有打印卖出的票,并且实现Runnable中的run ...

  8. Java之多线程窗口卖票问题(Runnable)

    /** * 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式 * 存在线程的安全问题,待解决. */class Window1 implements Runnable{ p ...

  9. Java之多线程窗口卖票问题(Thread)

    /** * * 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式 * * 存在线程的安全问题,待解决. * */class Window extends Thread{ priv ...

随机推荐

  1. General error 2006 MySQL server has gone away

    写入配置文件的办法: max_allowed_packet = 16M //但是这种有时候不支持,1024*1024*16这种有的也不支持 max_allowed_packet = 16777216 ...

  2. for循环计算阶乘

    int x = 10; for(int y = x - 1; y >= 1; y--) { x = x * y; } System.out.println(x); 从10乘到1的阶乘写法. lo ...

  3. LuoguP1126 机器人搬重物(BFS)

    题目链接:https://www.luogu.org/problemnew/show/P1126 思路:很不错的搜索题,用BFS,虐了我1天多才A掉 QAQ,细节很多. 1.每个状态包含行.列.方向. ...

  4. Android网络类型判断(2g、3g、wifi)

    判断网络类型是wifi,还是3G,还是2G网络,对不同 的网络进行不同的处理,现将判断方法整理给大家,以供参考   说明:下面用到的数据移动2G,联通2G,联通3G,wifi我都已经测试过,暂时手上 ...

  5. Hammer.js——给bootstrap添加触屏功能

    Hammer.js qq群号(html5技术交流):158677025   手机端演示二维码(或直接在手机中输入网址:http://lilinfeng.cncoder.me 浏览效果): 一.前言 移 ...

  6. CodeSmith生成SQL Server视图的实体类脚本/对应的生成模板

    C#生成sql视图的实体类 using System;using System.Text;using CodeSmith.Engine;using SchemaExplorer;using Syste ...

  7. (转)innodb 多版本并发控制原理详解

    转自:https://blog.csdn.net/aoxida/article/details/50689619 多版本并发控制技术已经被广泛运用于各大数据库系统中,如Oracle,MS SQL Se ...

  8. PAT 1055 集体照 (25)(STL-list+代码)

    1055 集体照 (25)(25 分)提问 拍集体照时队形很重要,这里对给定的N个人K排的队形设计排队规则如下: 每排人数为N/K(向下取整),多出来的人全部站在最后一排: 后排所有人的个子都不比前排 ...

  9. LUOGU P4408 [NOI2003]逃学的小孩(树的直径)

    题目描述 Chris家的电话铃响起了,里面传出了Chris的老师焦急的声音:“喂,是Chris的家长吗?你们的孩子又没来上课,不想参加考试了吗?”一听说要考试,Chris的父母就心急如焚,他们决定在尽 ...

  10. [SoapUI] Tips and Tricks(提示和技巧)

    https://www.soapui.org/scripting-properties/tips-tricks.html