Java学习--多线程案例--模拟火车票销售(线程安全问题)

概述:火车票20张,三个窗口同时卖火车票

一.创建一个Runnable接口的实现类TicketSell,重写run()方法


public class TicketSell implements Runnable{
    private int count = 20;  //票数20张

    @Override
    public void run() {
        Thread thread = Thread.currentThread();   //使用Thread的静态方法获取当前Thread对象
        while(true){

            if(count>0){

                try {    
                    Thread.sleep(200);   //相当于该线程被挂起,200ms后就绪再去抢占CPU运行  
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(thread.getName()+"  "+(--count));

            }
        }
    }

}


二.创建测试类,main方法中,创建三个Thread对象(三个窗口);

package com.blueSky.TicketDemo;

public class TicketTest {
    public static void main(String[] args) {
        TicketSell ticketSell = new TicketSell();  //3个窗口同卖20张票,使用一个Runnable接口的实现类

        //第一个窗口
        Thread thread1 = new Thread(ticketSell);
        thread1.setName("1号窗口");
        thread1.start();
        //第二个窗口
        Thread thread2 = new Thread(ticketSell);
        thread2.setName("2号窗口");
        thread2.start();
        //第三个窗口
        Thread thread3 = new Thread(ticketSell);
        thread3.setName("3号窗口");
        thread3.start();
    }
}

三.运行main方法进行测试

四 测试结果
Java学习--多线程案例--模拟火车票销售(线程安全问题)_第1张图片

五 问题分析

出现负数的原因:

进入条件后发生冲突,共享数据
睡眠相当于该线程被挂起,暂停执行,

* 假设此时count=1,线程1抢到CPU执行, 进入条件体,线程1休息200ms,该线程未执行count-1操作,
 *    所以下一个线程(比如线程3)抢到CPU执行时,count仍为1,仍会进入条件体,然后执行语句,睡眠200ms,
 *    当线程2到时,若两个线程还在睡眠(即都未执行count-1操作),count仍为1,线程3执行条件体,睡眠200ms,
 *    若之后线程1,睡眠时间到,且抢到了CPU执行,那么count-1  此时count=0
 *      然后线程2,3睡眠时间到,线程3抢到了CPU执行,那么count-1 此时count=0-1=-1     
 * *      然后线程3抢到了CPU ,那么执行count-1 此时count=-1-1=-2

六 .问题解决

使用同步锁,

  1. 使用同步代码块
package com.blueSky.TicketDemo;

public class TicketSell implements Runnable{
    private int count = 20;  //票数20张

    @Override
    public void run() {
        Thread thread = Thread.currentThread();   //使用Thread的静态方法获取当前Thread对象
        while(true){
            synchronized (this) {   //同步锁,同步代码块要把共享变量包进去
                if(count>0){
                    try {    
                        Thread.sleep(200);   
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(thread.getName()+"  "+(--count));
                }
            }
        }
    }

}

2 封装方法,方法上加synchronized修饰

package com.blueSky.TicketDemo;

public class TicketSell implements Runnable{
    private int count = 20;  //票数20张

    @Override
    public void run() {
        Thread thread = Thread.currentThread();   //使用Thread的静态方法获取当前Thread对象
        while(true){
//          synchronized (this) {
                ticketCount(thread);    //封装方法,方法上加synchronized进行修饰
//          }
        }
    }

    private synchronized void ticketCount(Thread thread) {  //方法上加synchronized进行修饰,该方法为同步方法,该方法同时只能有一个线程访问。
        if(count>0){
            try {    
                Thread.sleep(200);   
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(thread.getName()+"  "+(--count));
        }
    }

}

七. 同步锁好坏
优点:线程安全
缺点:效率低 (就像开关门要消耗时间一样)

你可能感兴趣的:(开发技术,线程,售票,线程安全,多线程)