需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口买票,请设计一个程序模拟该电影院卖票
思路:
1.定义一个类Ticket实现Runnable接口,里面定义一个成员变量:private int ticketCount = 100;
2.在Ticket类中重写run()方法实现卖票,代码步骤如下
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:票数要减1
C:卖光之后,线程停止
3.定义一个测试类TicketDemo,里面有main方法,代码步骤如下
A:创建Ticket类的对象
B:创建三个Thread类的对象,把Ticket对象作为构造方法的参数,并给出对应的窗口名称
C:启动线程
打印的顺序是随机的,是因为三个线程在争夺CPU运行权,这不影响程序的正确性
package threaddemo;
public class Demo {
public static void main(String[] args) {
/* Ticket ticket1 = new Ticket();
Ticket ticket2 = new Ticket();
Ticket ticket3 = new Ticket();
Thread t1 = new Thread();
Thread t2 = new Thread();
Thread t3 = new Thread();*/
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
//*******************************
package threaddemo;
public class Ticket implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
if (ticket == 0){
//卖完了
break;
}else {
ticket --;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket +"张票");
}
}
}
}
卖票案例的思考
刚才讲解了电影院卖票程序,好像没有什么问题。但是在实际生活中,售票时出票也是需要时间的,所以,在出售一张票的时候,需要一点时间的延迟,接下来我们去修改卖票的动作:每次出票时间100毫秒,用sleep()方法实现
加了延时之后
卖票出现了问题
相同的票出现了多次
出现了负数的票
package threaddemo;
//错误案例 窗口一在卖票,还剩下0张票
//窗口三在卖票,还剩下-1张票
//窗口二在卖票,还剩下-2张票
public class Ticket implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
if (ticket <= 0){
//卖完了
break;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket --;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket +"张票");
}
}
}
}
//**************************分隔符
package threaddemo;
//错误案例 窗口一在卖票,还剩下0张票
//窗口三在卖票,还剩下-1张票
//窗口二在卖票,还剩下-2张票
public class Ticket implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
if (ticket <= 0){
//卖完了
break;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket --;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket +"张票");
}
}
}
}
判断条件是 if (ticket == 0)的话,它有时候会一直执行,停不下来
package threaddemo;
//错误案例 窗口二在卖票,还剩下-167张票
//窗口一在卖票,还剩下-168张票
//窗口二在卖票,还剩下-169张票
//窗口三在卖票,还剩下-170张票
//窗口一在卖票,还剩下-171张票
//窗口二在卖票,还剩下-172张票
//窗口三在卖票,还剩下-173张票
public class Ticket implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
if (ticket == 0){
//卖完了
break;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket --;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket +"张票");
}
}
}
}
//************************
package threaddemo;
public class Demo {
public static void main(String[] args) {
/* Ticket ticket1 = new Ticket();
Ticket ticket2 = new Ticket();
Ticket ticket3 = new Ticket();
Thread t1 = new Thread();
Thread t2 = new Thread();
Thread t3 = new Thread();*/
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
卖票案例数据安全问题的解决
为什么会出现问题?(这也是我们判断多线程程序是否会有数据安全问题的标准)
多线程操作共享数据
如何解决多线程安全问题呢?
基本思想:让程序没有安全问题的环境
怎么实现呢?
把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
Java提供了同步代码块的方式来解决
同步代码块
锁多条语句操作共享数据,可以使用同步代码块实现
synchronized(任意对象){
多条语句操作共享数据的代码
}
默认情况是打开的,只要有一个线程进去执行代码了,锁就会关闭
同步的好处和弊端
好处:解决了多线程的数据安全问题
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
package threaddemo;
//错误案例 窗口二在卖票,还剩下-167张票
//窗口一在卖票,还剩下-168张票
//窗口二在卖票,还剩下-169张票
//窗口三在卖票,还剩下-170张票
//窗口一在卖票,还剩下-171张票
//窗口二在卖票,还剩下-172张票
//窗口三在卖票,还剩下-173张票
public class Ticket implements Runnable{
private int ticket = 100;
private Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (obj){//多个线程必须使用同一把锁
if (ticket == 0){
//卖完了
break;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket --;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket +"张票");
}
}
}
}
}
//********************************************
package threaddemo;
public class Demo {
public static void main(String[] args) {
/* Ticket ticket1 = new Ticket();
Ticket ticket2 = new Ticket();
Ticket ticket3 = new Ticket();
Thread t1 = new Thread();
Thread t2 = new Thread();
Thread t3 = new Thread();*/
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}