多线程应用:模拟多窗口售票 线程安全 同步synchronized

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013317445/article/details/82628961

某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,设计一个程序模拟该电影院售票。

//SaleTickets.java

public class SaleTickets implements Runnable {
    public SaleTickets(){}

    private int ticketsNumbers=100;
    private int personTimes=0;

    @Override
    public void run() {
        //while(true)模拟一直有票
        while(true){
            if(ticketsNumbers>0) {
                System.out.println(++personTimes);
                
                ticketsNumbers--;
                System.out.println(Thread.currentThread().getName() + "  " + "正在出售第:" + (100 - ticketsNumbers) + "张票  " + "余票:" + ticketsNumbers);

            }
        }
    }
}
public class SaleTicketsDemo {
    public static void main(String[] args){
        //这个只有一个对象 所以自然是共享100张票的
        //It's 资源对象
        SaleTickets saletic= new SaleTickets();

        //创建3个线程对象  3个线程去抢(java使用的线程调度模型是抢占式调度)
        Thread window1= new Thread(saletic, "1号窗口");
        Thread window2= new Thread(saletic, "2号窗口");
        Thread window3= new Thread(saletic, "3号窗口");

        window1.start();
        window2.start();
        window3.start();
    }
}
//代码测试结果:运行正常

考虑了延迟:
在run()方法加入了sleep:

public void run() {
        //while(true)模拟一直有票
        while(true){
            if(ticketsNumbers>0) {
                System.out.println(++personTimes);

                //模拟更真实的场景。正常是有延迟的
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                ticketsNumbers--;
                System.out.println(Thread.currentThread().getName() + "  " + "正在出售第:" + (100 - ticketsNumbers) + "张票  " + "余票:" + ticketsNumbers);

            }
        }

出现了问题
1.相同的一张票卖了多次:cpu的一次操作必须是原子性的
2.出现了负数票:随机性和延迟导致的

##如何解决线程安全问题
####哪些原因会导致出问题
多线程环境;
有共享数据;
有多条语句操作共享数据。

解决——同步代码块

思想:把操作共享数据的多条语句包成一个整体,当有线程在执行它时,不允许其他的线程再执行它。

synchronized(对象){ 
	需要同步的代码(即,多条语句操作共享数据的代码);
}

这个对象就如同的功能,所以要多个线程过来遇到的是同一把锁 ——>共享同一把锁。
同步代码块的对象是任意对象

public class SaleTickets implements Runnable {

    private int ticketsNumbers = 100;

    //定义同一把锁
    private Object lock = new Object();//Note:定义了一个private的对象

    @Override
    public void run() {

        while (true) {
            //三个线程都可以走到这里
            //假设window1此时抢到cpu的执行权,window1就要进来。门没锁,它可以进来。进来后就锁上了
            //假设window2抢到了cpu的执行权,window2要进来,但是门是锁着的,就要等着
            synchronized (lock) {
                if (ticketsNumbers > 0)
                    try {
                        Thread.sleep(100);//延迟
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                ticketsNumbers--;
                System.out.println(Thread.currentThread().getName()
                        + "  " + "正在出售第" + (100 - ticketsNumbers) + "张票");
                //窗口1正在售出第1张票
            }//window1出来了。开锁。
        }


    }
}

同步

同步的特点

前提:多个线程
多个线程使用的是同一个锁对象

同步的好处

同步的出现解决了多线程的安全问题。
####同步的弊端
当线程相当多时,因为每个线程都会去判断同步上的锁,这很耗费资源,无形中会降低程序的运行效率

同步的方式

两种方式:同步代码块、同步方法

同步代码块

同步方法

可以把同步关键字synchronized加在方法上(加在修饰符之后)。
同步方法的锁对象是:this
如果是静态方法,同步方法的锁对象是:类的字节码文件。
到底用谁呢?如果锁对象是this,就可以考虑使用同步方法。否则能使用同步代码块的尽量使用同步代码块。

猜你喜欢

转载自blog.csdn.net/u013317445/article/details/82628961